[Laszlo-checkins] r8515 - in openlaszlo/trunk: WEB-INF/lps/lfc/compiler WEB-INF/lps/lfc/core WEB-INF/lps/lfc/data WEB-INF/lps/lfc/views WEB-INF/lps/server/src/org/openlaszlo/compiler lps/components/base

ptw@openlaszlo.org ptw at openlaszlo.org
Tue Apr 1 19:37:21 PDT 2008


Author: ptw
Date: 2008-04-01 19:37:15 -0700 (Tue, 01 Apr 2008)
New Revision: 8515

Modified:
   openlaszlo/trunk/WEB-INF/lps/lfc/compiler/LzRuntime.lzs
   openlaszlo/trunk/WEB-INF/lps/lfc/core/LzNode.lzs
   openlaszlo/trunk/WEB-INF/lps/lfc/data/LzReplicationManager.lzs
   openlaszlo/trunk/WEB-INF/lps/lfc/views/LaszloCanvas.lzs
   openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/compiler/ClassCompiler.java
   openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/compiler/ClassModel.java
   openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/compiler/NodeModel.java
   openlaszlo/trunk/lps/components/base/basecomponent.lzx
Log:
Change 20080401-ptw-A by ptw at dueling-banjos.local on 2008-04-01 16:15:19 EDT
    in /Users/ptw/OpenLaszlo/ringding-clean
    for http://svn.openlaszlo.org/openlaszlo/trunk

Summary: Compile instances with methods as classes

Bugs Fixed:
LPP-5625 'Implement instances with methods as singleton classes'

Technical Reviewer: hqm (Message-ID: <8c61fad60804011441l41dae5ceucdbadfeb02b8b74b at mail.gmail.com>)
QA Reviewer: dda (Message-Id: <A3A3C1CC-2BFE-488A-858B-3DCA6BD2CF7D at ddanderson.com>)

Details:
    LzNode, NodeModel: Move $classrootdepth computation to compile-time.

    LzNode, LaszloCanvas, LzReplicationManager: Take care that
    _instanceAttrs may be null

    LzRuntime: Don't clobber global in swf9

    ClassCompiler, ClassModel: Move emitClassDeclaration to ClassModel

    NodeModel: Leave id as an attribute, declare named children as
    attributes so they can be resolved.  Predicate for nodes that have
    methods.  Compile nodes with methods as singleton classes.

    basecomponent:  Make the optional argument optional

Tests:
    smokecheck, lztest, Henry's checkbox test compiles



Modified: openlaszlo/trunk/WEB-INF/lps/lfc/compiler/LzRuntime.lzs
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/lfc/compiler/LzRuntime.lzs	2008-04-02 00:40:03 UTC (rev 8514)
+++ openlaszlo/trunk/WEB-INF/lps/lfc/compiler/LzRuntime.lzs	2008-04-02 02:37:15 UTC (rev 8515)
@@ -25,7 +25,11 @@
 /**
  * Define runtime module
  */
+if ($swf9) {
+$modules.runtime = global;
+} else {
 $modules.runtime = this;
+}
 
 /**
  * Define LZ module
@@ -45,7 +49,10 @@
  * Define global
  * TODO [2006-03-12 ptw] if the LFC is shared this needs to be multiplexed
  */
+if ($swf9) {
+} else {
 var global = $modules.user;
+}
 
 if ($debug) {
   /**

Modified: openlaszlo/trunk/WEB-INF/lps/lfc/core/LzNode.lzs
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/lfc/core/LzNode.lzs	2008-04-02 00:40:03 UTC (rev 8514)
+++ openlaszlo/trunk/WEB-INF/lps/lfc/core/LzNode.lzs	2008-04-02 02:37:15 UTC (rev 8515)
@@ -262,15 +262,6 @@
 
         var classChildren = this['constructor']['children'];
         if ( classChildren is Array ){
-            // classroot will not be defined for members of a state
-            // class, this is hard because the classroot is not a
-            // parent of its lexical children
-            // TODO: [2008-03-11 ptw] This should be computed at
-            // compile time
-            if (!classChildren['doneClassRoot'] && ! this.$isstate) {
-                classChildren.doneClassRoot=true;
-                this.__LZassignClassRoot(classChildren, 1);
-            }
             children = LzNode.mergeChildren(children, classChildren);
         }
 
@@ -1543,10 +1534,12 @@
    * constraint from
    */
   function releaseConstraint(attr:String) {
-    var c = this._instanceAttrs[attr];
-    if (c instanceof LzConstraintExpr) {
-      var m = c.methodName;
-      return this.releaseConstraintMethod(m);
+    if (this._instanceAttrs != null) {
+      var c = this._instanceAttrs[attr];
+      if (c instanceof LzConstraintExpr) {
+        var m = c.methodName;
+        return this.releaseConstraintMethod(m);
+      }
     }
     return false;
   }
@@ -2154,31 +2147,6 @@
 
 //+++++ Wacky class stuff
 
-
-/**
-  * called when the first instance of the class is instantiated
-  * must be called only once for each class
-  * @param arr: classChildren
-  * @param depth: call with depth=1 (recursive function)
-  * @access private
-  */
-function __LZassignClassRoot( arr , depth){
-    if (arr != null) {
-        var l = arr.length;
-        for ( var i = 0; i < l ; i++ ){
-            arr[ i ].attrs.$classrootdepth = depth;
-            var a = ('children' in arr[i]) ? arr[i].children : null;
-            if ( a && ('length' in a) ){
-                var cl = ConstructorMap[arr[i].name];
-                // note: when states are applied, they add their children as
-                // siblings therefore classChildren that appear within a state
-                // do not gain a level of depth
-                this.__LZassignClassRoot( a , (('$isstate' in cl.prototype) && (cl.prototype.$isstate)) ? depth : depth+1);
-            }
-        }
-    }
-}
-
 if ($swf9) {     // TODO [hqm 2008-03] This is just a debugging print utility function for swf9
 public static function objAsString(obj) {
     var s = "";

Modified: openlaszlo/trunk/WEB-INF/lps/lfc/data/LzReplicationManager.lzs
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/lfc/data/LzReplicationManager.lzs	2008-04-02 00:40:03 UTC (rev 8514)
+++ openlaszlo/trunk/WEB-INF/lps/lfc/data/LzReplicationManager.lzs	2008-04-02 02:37:15 UTC (rev 8515)
@@ -133,24 +133,26 @@
         return;
     }
 
-
     //this is so that when it looks like you're refering to the view in source
     //you can say view.datapath and you'll get what you want (which is this)
     this.datapath = this;
 
     //null, or name of view that was replicated
-    var name =  view._instanceAttrs.name;
+    var name =  view.name;
+    if (name != null) {
+      args.name = name;
+      // remove view we replace to avoid warning in LzNode.$lzc$set_name
+      delete view.immediateparent[name];
+      delete view.parent[name];
+    }
 
-    args.name = name;
-    // remove view we replace to avoid warning in LzNode.$lzc$set_name
-    delete view.immediateparent[name];
-    delete view.parent[name];
-
     //same deal for id
-    var id =  view._instanceAttrs.id;
-    args.id = id;
-    // remove any LzNode with this id
-    if (global[id] instanceof LzNode) global[id] = null;
+    var id =  view.id;
+    if (id != null) {
+      args.id = id;
+      // remove any LzNode with this id
+      if (global[id] instanceof LzNode) global[id] = null;
+    }
 
     //don't want to rerunxpath
     args.xpath = LzNode._ignoreAttribute;
@@ -249,7 +251,7 @@
 
     this.visible = odp.datacontrolsvisibility ||
         (!view.isinited &&
-            ('visible' in view._instanceAttrs) ? view._instanceAttrs.visible : view.visible);
+            (view._instanceAttrs != null && 'visible' in view._instanceAttrs) ? view._instanceAttrs.visible : view.visible);
 
     if ( args.pooling != null ){
         this.pooling = args.pooling;

Modified: openlaszlo/trunk/WEB-INF/lps/lfc/views/LaszloCanvas.lzs
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/lfc/views/LaszloCanvas.lzs	2008-04-02 00:40:03 UTC (rev 8514)
+++ openlaszlo/trunk/WEB-INF/lps/lfc/views/LaszloCanvas.lzs	2008-04-02 02:37:15 UTC (rev 8515)
@@ -404,18 +404,19 @@
 function initDone (){
     //reorder initial subviews so preloaded stuff is first
     var sva = new Array;
+    var is = this._lzinitialsubviews;
 
-    for ( var i = 0; i < this._lzinitialsubviews.length; i++ ){
-        if ( 'initimmediate' in this._lzinitialsubviews[ i ].attrs && 
-             this._lzinitialsubviews[ i ].attrs.initimmediate ){
-            sva.push( this._lzinitialsubviews[ i ] );
+    for ( var i = 0; i < is.length; i++ ){
+        var isi = is[i];
+        if ( isi['attrs'] && isi.attrs['initimmediate'] ){
+            sva.push( isi );
         }
     }
 
-    for ( var i = 0; i < this._lzinitialsubviews.length; i++ ){
-        if ( !('initimmediate' in this._lzinitialsubviews[ i ].attrs && 
-             this._lzinitialsubviews[ i ].attrs.initimmediate) ){
-            sva.push( this._lzinitialsubviews[ i ] );
+    for ( var i = 0; i < is.length; i++ ){
+        var isi = is[i];
+        if ( ! ( isi['attrs'] && isi.attrs['initimmediate'] ) ){
+            sva.push( isi );
         }
     }
 

Modified: openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/compiler/ClassCompiler.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/compiler/ClassCompiler.java	2008-04-02 00:40:03 UTC (rev 8514)
+++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/compiler/ClassCompiler.java	2008-04-02 02:37:15 UTC (rev 8515)
@@ -23,7 +23,7 @@
 
 /** Compiler for <code>class</code> class elements.
  */
-class ClassCompiler extends ViewCompiler {
+class ClassCompiler extends ViewCompiler  {
     /**
        For a declaration of a class named "foobar"
        
@@ -235,38 +235,6 @@
         schema.addElement(element, classname, superclass, attributeDefs, mEnv);
     }
     
-    // Map of LFC tag names
-    static HashMap LFCTagTable = new HashMap();
-    static {
-      LFCTagTable.put("node", "LzNode");
-      LFCTagTable.put("view", "LzView");
-      LFCTagTable.put("text", "LzText");
-      LFCTagTable.put("inputtext", "LzInputText");
-      LFCTagTable.put("canvas", "LzCanvas");
-      LFCTagTable.put("script", "LzScript");
-      LFCTagTable.put("animatorgroup", "LzAnimatorGroup");
-      LFCTagTable.put("animator", "LzAnimator");
-      LFCTagTable.put("layout", "LzLayout");
-      LFCTagTable.put("state", "LzState");
-      LFCTagTable.put("command", "LzCommand");
-      LFCTagTable.put("selectionmanager", "LzSelectionManager");
-      LFCTagTable.put("dataselectionmanager", "LzDataSelectionManager");
-      LFCTagTable.put("datapointer", "LzDataPointer");
-      LFCTagTable.put("dataprovider", "LzDataProvider");
-      LFCTagTable.put("datapath", "LzDatapath");
-      LFCTagTable.put("dataset", "LzDataset");
-      LFCTagTable.put("datasource", "LzDatasource");
-      LFCTagTable.put("lzhttpdataprovider", "LzHTTPDataProvider");
-    }
-
-    public static String tagToClass(String s) {
-      if (LFCTagTable.containsKey(s)) {
-        return (String)(LFCTagTable.get(s));
-      }
-      String lzcPackagePrefix = "$lzc$class_";
-      return lzcPackagePrefix + s;
-    }    
-
     public void compile(Element elt) {
         String tagName = elt.getAttributeValue("name");
         ViewSchema schema = mEnv.getSchema();
@@ -284,148 +252,17 @@
                     , elt);
             }
         }
-        String className = tagToClass(tagName);
-        // className will be a global
-        mEnv.addId(className, elt);
-
-        ClassModel classModel = schema.getClassModel(tagName);
-        
-        String linedir = CompilerUtils.sourceLocationDirective(elt, true);
-        ViewCompiler.preprocess(elt, mEnv);
-        
         // We compile a class declaration just like a view, and then
         // add attribute declarations and perhaps some other stuff that
         // the runtime wants.
+        ClassModel classModel = schema.getClassModel(tagName);
+        ViewCompiler.preprocess(elt, mEnv);
         NodeModel model = NodeModel.elementAsModel(elt, schema, mEnv);
         model = model.expandClassDefinitions();
+        // Establish class root
+        model.assignClassRoot(0);
         classModel.setNodeModel(model);
-        // Should the package prefix be in the model?  Should the
-        // model store class and tagname separately?
-        String supertagname = classModel.getSuperclassName();
-        String superclassname = tagToClass(supertagname);
-        ClassModel superclassModel = schema.getClassModel(supertagname);
-
-
-        // Build the constructor
-        String body = "";
-        body += "super(parent, attrs, children, async);\n";
-
-        model.setAttribute(
-          className,
-          new Function(
-                className,
-                // All nodes get these args when constructed
-                // Apparently AS3 does not allow defaulting of
-                // primitive args
-//                 "parent:LzNode, attrs:Object, children:Object? = null, async:boolean = false",
-                "parent:LzNode? = null, attrs:Object? = null, children:Object? = null, async:Boolean = false",
-                body,
-                null));
-
-        // Build the class body
-        String classBody = "";
-        // Set the tag name
-        model.setClassAttribute("tagname",  ScriptCompiler.quote(tagName));
-
-        // --- This should only be for subclasses of Node
-        String children = ScriptCompiler.objectAsJavascript(model.childrenMaps());
-        // class#classChildren now class.children
-        model.setClassAttribute("children", "LzNode.mergeChildren(" + children + ", " + superclassname + "['children'])");
-
-        // Declare all instance vars and methods, save initialization
-        // in <class>.attributes
-        Map attrs = model.getAttrs(); // classModel.getMergedAttributes();
-        Map setters = classModel.getMergedSetters();
-        Map decls = new LinkedHashMap();
-        Map inits = new LinkedHashMap();
-        boolean isstate = classModel.isSubclassOf(schema.getClassModel("state"));
-        for (Iterator i = attrs.entrySet().iterator(); i.hasNext(); ) {
-          Map.Entry entry = (Map.Entry) i.next();
-          String key = (String) entry.getKey();
-          Object value = entry.getValue();
-          boolean redeclared = (superclassModel.getAttribute(key) != null);
-          if ((value instanceof NodeModel.BindingExpr)) {
-              // Bindings always have to be installed as an init
-              if (! redeclared) {
-                  decls.put(key, null);
-              }
-              inits.put(key, ((NodeModel.BindingExpr)value).getExpr());
-          } else if (value instanceof Function &&
-                     ((! isstate) ||
-                      className.equals(key))) {
-            // Methods are just decls.  Except in states, because they
-            // have to be applied to the parent, except for the
-            // constructor!
-            decls.put(key, value);
-          } else if (value != null) {
-            // If this is a re-declared attribute, we just init it,
-            // don't re-declare it
-            if (superclassModel.getAttribute(key) != null) {
-                inits.put(key, value);
-            }
-            // If there is a setter for this attribute, or this is a
-            // state, or this is an Array or Map argument that needs
-            // magic merging, the value has to be installed as an init,
-            // otherwise it should be installed as a decl
-            //
-            // TODO: [2008-03-15 ptw] This won't work until we know
-            // (in the classModel) the setters for the built-in
-            // classes, so we install as an init for now and this is
-            // fixed up in LzNode by installing inits that have no
-            // setters when the arguments are merged
-
-            if (true) { // (! (value instanceof String))  || setters.containsKey(key) || isstate) {
-              if (! redeclared) {
-                decls.put(key, null);
-              }
-              inits.put(key, value);
-            } else {
-              if (! redeclared) {
-                decls.put(key, value);
-                // If there is a property that would have been shadowed,
-                // you have to hide that from applyArgs, or you will get
-                // clobbered!
-                inits.put(key, "LzNode._ignoreAttribute");
-              } else {
-                inits.put(key, value);
-              }
-            }
-          } else {
-            // Just a declaration
-              if (! redeclared) {
-                decls.put(key, value);
-            }
-          }
-        }
-        // mergedAttrs does not deal with our methods
-//         Map methods = model.getAttrs();
-//         for (Iterator i = methods.entrySet().iterator(); i.hasNext(); ) {
-//           Map.Entry entry = (Map.Entry) i.next();
-//           String key = (String) entry.getKey();
-//           Object value = entry.getValue();
-//           if ((value instanceof Function)) {
-//             decls.put(key, value);
-//           }
-//         }
-        model.setClassAttribute("attributes", "new LzInheritedHash(" + superclassname + ".attributes)");
-        classBody += "LzNode.mergeAttributes(" +
-          ScriptCompiler.objectAsJavascript(inits) +
-          ", " + className + ".attributes);\n";
-
-        Map viewMap = model.asMap();
-        // Put in the class name, not "class"
-        viewMap.put("name", ScriptCompiler.quote(className));
-
-        ScriptClass scriptClass =
-          new ScriptClass(className,
-                          superclassname,
-                          decls,
-                          (Map)viewMap.get("classAttrs"),
-                          classBody);
-
-        mEnv.compileScript(scriptClass.toString(), elt);
-        // Install in constructor map
-        mEnv.compileScript("ConstructorMap[" + ScriptCompiler.quote(tagName) + "] = " + className + ";\n");
+        classModel.emitClassDeclaration(mEnv);
     }
 
   protected void compileClass(Element elt, ClassModel classModel, String initobj) {

Modified: openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/compiler/ClassModel.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/compiler/ClassModel.java	2008-04-02 00:40:03 UTC (rev 8514)
+++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/compiler/ClassModel.java	2008-04-02 02:37:15 UTC (rev 8515)
@@ -8,9 +8,12 @@
 import java.util.*;
 import org.jdom.Element;
 import org.openlaszlo.sc.Function;
+import org.openlaszlo.sc.ScriptCompiler;
+import org.openlaszlo.sc.ScriptClass;
 
 class ClassModel implements Comparable {
     protected final ViewSchema schema;
+    /** This is really the LZX tag name */
     protected final String className;
     // This is null for builtin classes
     protected final ClassModel superclass;
@@ -90,7 +93,160 @@
     return lzx;
   }
 
-    
+  // Map of LFC tag names
+  static HashMap LFCTag2JSClass = new HashMap();
+  static {
+    LFCTag2JSClass.put("node", "LzNode");
+    LFCTag2JSClass.put("view", "LzView");
+    LFCTag2JSClass.put("text", "LzText");
+    LFCTag2JSClass.put("inputtext", "LzInputText");
+    LFCTag2JSClass.put("canvas", "LzCanvas");
+    LFCTag2JSClass.put("script", "LzScript");
+    LFCTag2JSClass.put("animatorgroup", "LzAnimatorGroup");
+    LFCTag2JSClass.put("animator", "LzAnimator");
+    LFCTag2JSClass.put("layout", "LzLayout");
+    LFCTag2JSClass.put("state", "LzState");
+    LFCTag2JSClass.put("command", "LzCommand");
+    LFCTag2JSClass.put("selectionmanager", "LzSelectionManager");
+    LFCTag2JSClass.put("dataselectionmanager", "LzDataSelectionManager");
+    LFCTag2JSClass.put("datapointer", "LzDatapointer");
+    LFCTag2JSClass.put("dataprovider", "LzDataProvider");
+    LFCTag2JSClass.put("datapath", "LzDatapath");
+    LFCTag2JSClass.put("dataset", "LzDataset");
+    LFCTag2JSClass.put("datasource", "LzDatasource");
+    LFCTag2JSClass.put("lzhttpdataprovider", "LzHTTPDataProvider");
+  }
+
+  public static String LZXTag2JSClass(String s) {
+    if (LFCTag2JSClass.containsKey(s)) {
+      return (String)(LFCTag2JSClass.get(s));
+    }
+    String lzcPackagePrefix = "$lzc$class_";
+    return lzcPackagePrefix + s;
+  }
+
+  /**
+   * Emits a class model as a JS2 class declaration.  This is used
+   * both by the class compiler and the instance compiler (when an
+   * instance has methods, either explicit or implicit).
+   */
+  void emitClassDeclaration(CompilationEnvironment env) {
+    String tagName = getClassName();
+    String className = LZXTag2JSClass(tagName);
+    // className will be a global
+    env.addId(className, definition);
+    // Should the package prefix be in the model?  Should the
+    // model store class and tagname separately?
+    ClassModel superclassModel = getSuperclassModel();
+    String superTagName = superclassModel.getClassName();
+    String superclassName = LZXTag2JSClass(superTagName);
+
+    // Build the constructor
+    String body = "";
+    body += "super($lzc$parent, $lzc$attrs, $lzc$children, $lzc$async);\n";
+    nodeModel.setAttribute(
+      className,
+      new Function(
+        className,
+        // All nodes get these args when constructed
+        // Apparently AS3 does not allow defaulting of
+        // primitive args
+        "$lzc$parent:LzNode? = null, $lzc$attrs:Object? = null, $lzc$children:Array? = null, $lzc$async:Boolean = false",
+        body,
+        null));
+
+    // Build the class body
+    String classBody = "";
+    // Set the tag name
+    nodeModel.setClassAttribute("tagname",  ScriptCompiler.quote(tagName));
+
+    // --- This should only be for subclasses of Node
+    String children = ScriptCompiler.objectAsJavascript(nodeModel.childrenMaps());
+    // class#classChildren now class.children
+    nodeModel.setClassAttribute("children", "LzNode.mergeChildren(" + children + ", " + superclassName + "['children'])");
+
+    // Declare all instance vars and methods, save initialization
+    // in <class>.attributes
+    Map attrs = nodeModel.getAttrs(); // classModel.getMergedAttributes();
+    Map setters = getMergedSetters();
+    Map decls = new LinkedHashMap();
+    Map inits = new LinkedHashMap();
+    boolean isstate = isSubclassOf(schema.getClassModel("state"));
+    for (Iterator i = attrs.entrySet().iterator(); i.hasNext(); ) {
+      Map.Entry entry = (Map.Entry) i.next();
+      String key = (String) entry.getKey();
+      Object value = entry.getValue();
+      boolean redeclared = (superclassModel.getAttribute(key) != null);
+      if ((value instanceof NodeModel.BindingExpr)) {
+        // Bindings always have to be installed as an init
+        if (! redeclared) {
+          decls.put(key, null);
+        }
+        inits.put(key, ((NodeModel.BindingExpr)value).getExpr());
+      } else if (value instanceof Function &&
+                 ((! isstate) ||
+                  className.equals(key))) {
+        // Methods are just decls.  Except in states, because they
+        // have to be applied to the parent, except for the
+        // constructor!
+        decls.put(key, value);
+      } else if (value != null) {
+        // If this is a re-declared attribute, we just init it,
+        // don't re-declare it
+        if (superclassModel.getAttribute(key) != null) {
+          inits.put(key, value);
+        }
+        // If there is a setter for this attribute, or this is a
+        // state, or this is an Array or Map argument that needs
+        // magic merging, the value has to be installed as an init,
+        // otherwise it should be installed as a decl
+        //
+        // TODO: [2008-03-15 ptw] This won't work until we know
+        // (in the classModel) the setters for the built-in
+        // classes, so we install as an init for now and this is
+        // fixed up in LzNode by installing inits that have no
+        // setters when the arguments are merged
+        if (true) { // (! (value instanceof String))  || setters.containsKey(key) || isstate) {
+          if (! redeclared) {
+            decls.put(key, null);
+          }
+          inits.put(key, value);
+        } else {
+          if (! redeclared) {
+            decls.put(key, value);
+            // If there is a property that would have been shadowed,
+            // you have to hide that from applyArgs, or you will get
+            // clobbered!
+            inits.put(key, "LzNode._ignoreAttribute");
+          } else {
+            inits.put(key, value);
+          }
+        }
+      } else {
+        // Just a declaration
+        if (! redeclared) {
+          decls.put(key, value);
+        }
+      }
+    }
+    // Create inits list, merged with superclass inits
+    nodeModel.setClassAttribute("attributes", "new LzInheritedHash(" + superclassName + ".attributes)");
+    classBody += "LzNode.mergeAttributes(" +
+      ScriptCompiler.objectAsJavascript(inits) +
+      ", " + className + ".attributes);\n";
+
+    // Emit the class decl
+    ScriptClass scriptClass =
+      new ScriptClass(className,
+                      superclassName,
+                      decls,
+                      nodeModel.getClassAttrs(),
+                      classBody);
+    env.compileScript(scriptClass.toString(), definition);
+    // Install in constructor map
+    env.compileScript("ConstructorMap[" + ScriptCompiler.quote(tagName) + "] = " + className + ";\n");
+  }
+
     /** Returns true if this is equal to or a subclass of
      * superclass. */
     boolean isSubclassOf(ClassModel superclass) {
@@ -163,10 +319,12 @@
     return merged;
   }
 
+    /** This is really the LZX tag name */
     String getClassName () {
      return this.className;
     }
     
+    /** This is really the LZX tag name */
     String getSuperclassName() {
         if (superclassName != null) {
             return superclassName; 

Modified: openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/compiler/NodeModel.java
===================================================================
--- openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/compiler/NodeModel.java	2008-04-02 00:40:03 UTC (rev 8514)
+++ openlaszlo/trunk/WEB-INF/lps/server/src/org/openlaszlo/compiler/NodeModel.java	2008-04-02 02:37:15 UTC (rev 8515)
@@ -816,11 +816,13 @@
                 type = ViewSchema.EXPRESSION_TYPE;
             }
 
-            if (type == schema.ID_TYPE) {
-                this.id = value;
-            } else if (type == schema.EVENT_HANDLER_TYPE) {
+            if (type == schema.EVENT_HANDLER_TYPE) {
                 addHandlerFromAttribute(element, name, value);
             } else {
+                // NOTE: [2008-04-01 ptw] May be obsolete?
+                if (type == schema.ID_TYPE) {
+                    this.id = value;
+                }
                 String when = this.getAttributeValueDefault(
                     name, "when", WHEN_IMMEDIATELY);
                 // NYI
@@ -1061,6 +1063,13 @@
                     checkChildNameConflict(element.getName(), child, env);
                     NodeModel childModel = elementAsModel(child, schema, env);
                     children.add(childModel);
+                    // Declare the child name (if any) as a property
+                    // of the node so that references to it from
+                    // methods can be resolved at compile-time
+                    String childName = child.getAttributeValue("name");
+                    if (childName != null) {
+                        attrs.put(childName, null);
+                    }
                     totalSubnodes += childModel.totalSubnodes();
                 }
             } catch (CompilationError e) {
@@ -1800,41 +1809,83 @@
       return attrs;
     }
 
+    boolean hasMethods() {
+        for (Iterator i = attrs.values().iterator(); i.hasNext(); ) {
+            if (i.next() instanceof Function) { return true; }
+        }
+        return false;
+    }
+
+    Map getClassAttrs() {
+      return classAttrs;
+    }
+
     Map getSetters() {
         return setters;
     }
 
     Map asMap() {
+        updateAttrs();
         Map map = new LinkedHashMap();
-        updateAttrs();
-        map.put("name", ScriptCompiler.quote(className));
-        Map inits = new LinkedHashMap();
-        // Node as map just wants to see all the attrs, so clean out
-        // the binding markers
-        for (Iterator i = attrs.entrySet().iterator(); i.hasNext(); ) {
-          Map.Entry entry = (Map.Entry) i.next();
-          String key = (String) entry.getKey();
-          Object value = entry.getValue();
-          if (! (value instanceof NodeModel.BindingExpr)) {
-            inits.put(key, value);
-          } else {
-            inits.put(key, ((NodeModel.BindingExpr)value).getExpr());
-          }
+        String tagName = className;
+        if (hasMethods()) {
+            // Emit a class declaration to hold the methods
+            String name = id;
+            if (name == null) {
+                name = CompilerUtils.attributeUniqueName(element, "class");
+            }
+            tagName = className + "_" + name;
+            ClassModel classModel = new ClassModel(tagName, parentClassModel, schema, element);
+            classModel.setNodeModel(this);
+            classModel.emitClassDeclaration(env);
+        } else {
+            Map inits = new LinkedHashMap();
+            // Node as map just wants to see all the attrs, so clean out
+            // the binding markers
+            for (Iterator i = attrs.entrySet().iterator(); i.hasNext(); ) {
+                Map.Entry entry = (Map.Entry) i.next();
+                String key = (String) entry.getKey();
+                Object value = entry.getValue();
+                if (! (value instanceof NodeModel.BindingExpr)) {
+                    inits.put(key, value);
+                } else {
+                    inits.put(key, ((NodeModel.BindingExpr)value).getExpr());
+                }
+            }
+            if (!attrs.isEmpty()) {
+                map.put("attrs", inits);
+            }
+            if (!classAttrs.isEmpty()) {
+                map.put("classAttrs", classAttrs);
+            }
+            if (!children.isEmpty()) {
+                map.put("children", childrenMaps());
+            }
         }
-        map.put("attrs", inits);
-        if (!classAttrs.isEmpty()) {
-            map.put("classAttrs", classAttrs);
-        }
+
+        // The tag to instantiate
+        // TODO: [2008-04-01 ptw] we could have a flag day and put the
+        // class here, eliminating having to go through the
+        // constructor map...
+        map.put("name", ScriptCompiler.quote(tagName));
         if (id != null) {
+            // NOTE: [2008-04-01 ptw] May be obsolete?
             map.put("id", ScriptCompiler.quote(id));
-            inits.put("id", ScriptCompiler.quote(id));
         }
-        if (!children.isEmpty()) {
-            map.put("children", childrenMaps());
-        }
+
         return map;
     }
 
+    void assignClassRoot(int depth) {
+        if (! parentClassModel.isSubclassOf(schema.getClassModel("state"))) { depth++; }
+        Integer d = new Integer(depth);
+        for (Iterator i = children.iterator(); i.hasNext(); ) {
+            NodeModel child = (NodeModel)i.next();
+            child.attrs.put("$classrootdepth", d);
+            child.assignClassRoot(depth);
+        }
+    }
+
     List childrenMaps() {
         List childMaps = new Vector(children.size());
         for (Iterator iter = children.iterator(); iter.hasNext(); )

Modified: openlaszlo/trunk/lps/components/base/basecomponent.lzx
===================================================================
--- openlaszlo/trunk/lps/components/base/basecomponent.lzx	2008-04-02 00:40:03 UTC (rev 8514)
+++ openlaszlo/trunk/lps/components/base/basecomponent.lzx	2008-04-02 02:37:15 UTC (rev 8515)
@@ -299,11 +299,10 @@
                    0xddddff) 
               <br/>param Number brightness: -255 to 255, optional parameter
                    will lighten or darken everything that is colorized -->
-        <method name="setTint" args="v, color, brightness">
+        <method name="setTint" args="v, color, brightness=0">
             <![CDATA[
             if (v.capabilities.colortransform) {
             if (color != "" && color != null){
-                if (brightness == null) { brightness = 0; }
                 var rgb = color;
                 var red=(rgb >> 16) & 0xFF;
                 var green=(rgb >> 8) & 0xFF;



More information about the Laszlo-checkins mailing list