Program Editor Example
The program editor is the first real application written in StrataCode. It's a bit too large to show here in its entirety but worth looking at the overall structure, and some key code slices. It's the best example of how StrataCode scales to difficult problems.

The swing version

The schtml version

The program editor lives in the editor layer group. It consists of several layers that total ~8000 lines of code. It uses 350 forward binding expressions, 20 bi-directional bindings, 100 reverse bindings, and 150 object definitions.

It runs fully compiled or fully dynamic hybrid modes where the model is compiled and the UI is dynamic or vice versa. The swing versionis demoed in both the Basic and Advanced demos listed in the videos. The web framework has 2/3s of the functionality of the swing version working but soon will work like the swing version but in the browser. You can view types flexibly in merged and layered views, control visibility based on code-complexity: declarative, application, framework or code-role: UI, database, admin, style, application. In dynamic mode, when you change properties on interfaces using multiple inheritance, they propagate throughout the initialization tree. You can create or remove objects, properties, or layers all without restarting. When a restart is necessary, the restart icon lights up, a reason box describes the changes which requires a restart.

The swing user interface code is longer because it uses data binding for all layout (not ideal but not half bad!) The JS version is only 2/3rd done but will end up doing the same thing with about half the code.

There are substantial cross platform code savings. Building the client/server app involved splitting up the existing classes into pieces that are put into new layers. Code which depends on swing goes into the swing specific layer. Code that depends on the server features, goes into that layer. Code that does not depend on anything which is generic to all goes into the model layer.

Additionally, the complex data structures put into this model layer, which is shared by both the client and server runtimes, can serialize themselves back and forth seamlessly.

Here's the layer of EditorModel that is synchronized - i.e. shared on client and server:

file: editor/model/EditorModel.sc
import sc.bind.Bind;
import sc.sync.SyncManager;
import sc.obj.Sync;
import sc.obj.SyncMode;
import sc.lang.html.Element;

/** 
   The main view model object for viewing and editing of the program model.  It exposes
   the current selection and provides access to the currently selected property, types and layers. 
   */
class EditorModel implements sc.bind.IChangeable {
   /** Specifies the list of types for the model */
   String[] typeNames = new String[0];

   String[] oldTypeNames; // Last time model was rebuilt

   String createModeTypeName; // Set when in createMode and a type is selected

   LayeredSystem system;

   int windowState = 0; // 0 = iconfied, 1 = open, 2 = maximized

   /** Set to the current layer */
   Layer currentLayer :=: ctx.currentLayer;

   /** The current type */
   Object currentType;

   /** The current Java model for the type */
   JavaModel currentJavaModel;

   /** When a property has focus, set to the property */
   Object currentProperty;

   UIIcon currentPropertyIcon;

   /** The enclosing type of the current property */
   Object currentPropertyType;

   /** The currentPropertyType filtered based on the imported type name */
   String importedPropertyType;

   /** Set this to search for a current type - if found, it is set and currentType is changed */
   String currentTypeSearch;

   /** If there's a selected instance, the instance */
   Object currentInstance;

   /** When the add/minus button is pressed, this gets toggled */
   boolean createMode = false;

   String currentPropertyOperator;

   String savedPropertyOperator;

   /** Property value bound to the current text field - updated live */
   String currentPropertyValue;

   /** Set to the currently selected package, or if a type is selected, the package of that type */
   String currentPackage;

   /** Last known value from the model for the property */
   String savedPropertyValue;

   /** Set to false if we only use types from the current layer.  Otherwise, merge all layers behind the current layer */
   boolean mergeLayers = false;

   /** Set to true when the current type is a layer */
   boolean currentTypeIsLayer;

   /** Set to true for an object or class to show properties from its extends class */
   boolean inherit = false;

   /** Incremented each time the selection changes so we can update any one who depends on the selection from a central event. */
   int selectionChanged = 0;

   /** Set to true when changes have been made to source files in the current runtime which require a process restart. */
   boolean staleCompiledModel;

   /** True when we the current property is an editable one. */
   boolean editSelectionEnabled = false;

   /** Generated values, kept in sync when you change typeNames and currentLayer */
   ArrayList<Object> types;                 // The global list of just the selected types
   ArrayList<Object> inheritedTypes;        // Like types only includes any inherited types as well when inherit is true
   @Sync(syncMode=SyncMode.Disabled)
   ArrayList<Object> filteredTypes;         // The merged list of the most specific type in the current selected set of types/layers
   ArrayList<Layer> typeLayers;             // The list of layers which define the types
   @Sync(syncMode=SyncMode.Disabled)
   ArrayList<Layer> filteredTypeLayers;     // The list of layers which define the types based on currentLayer/mergeLayers flags - used for 3d view
   @Sync(syncMode=SyncMode.Disabled)
   ArrayList<List<Object>> typesPerLayer;   // For each layer in the current set, the set of types in this layer - used for 3d view
   @Sync(syncMode=SyncMode.Disabled)
   Map<String, List<Object>> filteredTypesByLayer;   // For each selected type, the list of types for each selected layer - used for 3d view
   ArrayList<Object> visibleTypes;          // Used in form view - filters the set of types by the merge and inherited 

   class SelectedFile {
      SrcEntry file;
      List<Object> types;
      JavaModel model;
      Layer layer; // Layer where this file was selected.  if the layer is transparent, it may not be the same as the model's layer

      Layer getModelLayer() {
         return model.layer;
      }
   }

   LinkedHashMap<String, SelectedFile> selectedFileIndex; // Groups selected, filtered types by the files they live in for the code view
   ArrayList<SelectedFile> selectedFileList;

   ArrayList<CodeType> codeTypes = new ArrayList(CodeType.allSet);

   ArrayList<CodeFunction> codeFunctions = new ArrayList(EnumSet.allOf(CodeFunction.class));

   EditorContext ctx;

   boolean triggeredByUndo; // When a type change occurs because of an undo operation we do not want to record that op in the redo list again.

   void changeCodeFunctions(EnumSet<CodeFunction> cfs) {
      codeFunctions = new ArrayList(cfs);
   }

   void updateCodeFunction(CodeFunction cf, boolean add) {
      if (add) {
         if (!codeFunctions.contains(cf))
            codeFunctions.add(cf);
      }
      else
         codeFunctions.remove(cf);
   }

   boolean isTypeNameSelected(String typeName) {
      if (typeName == null)
         return false;

      for (String tn:typeNames)
         if (tn.equals(typeName))
            return true;

      return false;
   }

   boolean isCreateModeTypeNameSelected(String typeName) {
      if (createModeTypeName == null)
         return false;

      return createModeTypeName.equals(typeName);
   }

   void changeCurrentType(Object type) {
      if (type == null || type == currentType)
         return;

      String[] newTypeNames = new String[1];
      newTypeNames[0] = DynUtil.getTypeName(type, true);
      typeNames = newTypeNames;

      currentType = type;

      selectionChanged++;
   }

   void clearCurrentType() {
      typeNames = new String[0];
      currentType = null;
   }

   void changeCurrentTypeName(String typeName) {
      String[] newTypeNames = new String[1];
      newTypeNames[0] = typeName;
      typeNames = newTypeNames;
   }

   String getPropertySelectionName() {
      return " Property";
   }

   String getTypeSelectionName() {
      return DynUtil.isObject(currentType) ? " Object" : " Class";
   }

   String getCurrentSelectionName() {
      if (currentProperty != null) {
         return getPropertySelectionName();
      }
      else if (currentTypeIsLayer) {
         if (currentLayer.dynamic)
            return " Dynamic Layer";
         else
            return " Compiled Layer";

      }
      else if (currentType != null)
         return getTypeSelectionName();
      else
         return null;
   }

   // These should all be removed when remote methods are implemented.
   void deleteCurrentProperty() {
      System.err.println("*** ERROR: remote method not implemented");
   }

   void deleteCurrentLayer() {
      System.err.println("*** ERROR: remote method not implemented");
   }

   void deleteCurrentType() {
      System.err.println("*** ERROR: remote method not implemented");
   }

   void removeLayers(ArrayList<Layer> layers) {
      System.err.println("*** Implement as a remote method");
   }

   boolean getDebugBindingEnabled() {
      return Bind.trace;
   }

   void setDebugBindingEnabled(boolean de) {
      if (system != null && system.options != null)
         system.options.verbose = de;
      Bind.trace = de;
   }

   void toggleDebugBindingEnabled() {
      setDebugBindingEnabled(!getDebugBindingEnabled());
   }

   boolean getDebugHTMLEnabled() {
      return Element.trace;
   }

   void setDebugHTMLEnabled(boolean de) {
      Element.trace = de;
   }

   void toggleDebugHTMLEnabled() {
      setDebugHTMLEnabled(!getDebugHTMLEnabled());
   }

   boolean getDebugSyncEnabled() {
      return SyncManager.trace;
   }

   void setDebugSyncEnabled(boolean de) {
      SyncManager.trace = de;
   }

   void toggleDebugSyncEnabled() {
      setDebugSyncEnabled(!getDebugSyncEnabled());
   }

   abstract Object[] getPropertiesForType(Object type);

   boolean enableUpdateProperty := !DynUtil.equalObjects(currentPropertyValue, savedPropertyValue) ||
                                   !DynUtil.equalObjects(currentPropertyOperator, savedPropertyOperator); 

   //abstract String setElementValue(Object type, Object inst, Object prop, String expr, boolean updateInstances, boolean valueIsExpr);

}
Here the synchronized layer of TypeTreeModel:
file: editor/model/TypeTreeModel.sc
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.EnumSet;
import java.util.Collections;

import sc.layer.Layer;
import sc.layer.SrcEntry;

import sc.util.ArrayList;
import sc.util.LinkedHashMap;
import sc.util.StringUtil;
import sc.type.CTypeUtil;

import sc.obj.Constant;

import sc.lang.java.ModelUtil;

import sc.layer.CodeType;
import sc.layer.CodeFunction;

import sc.dyn.DynUtil;

import sc.sync.SyncManager;

@sc.obj.Component
class TypeTreeModel {
   EditorModel editorModel;
   LayeredSystem system;

   ArrayList<CodeType> codeTypes :=: editorModel.codeTypes;
   ArrayList<CodeFunction> codeFunctions :=: editorModel.codeFunctions;

   // Adds to the set of layers you include in the index.  These will be in active layers.
   String[] specifiedLayerNames;

   boolean createMode = false;
   boolean propertyMode = false; // when create mode is true, are we creating properties or types?

   boolean addLayerMode = false;  // Exclusive with the other two
   boolean createLayerMode = false; // When layerMode is true, are we including or creating?
   boolean layerMode := createLayerMode || addLayerMode;

   transient boolean valid = true;
   transient boolean rebuildFirstTime = true;
   transient boolean refreshInProgress = false;

   transient boolean typeTreeBuilt = false, layerTreeBuilt = false;

   // These are the two main trees of TreeEnt objects.  This tree is not directly displayed but is referenced from the TreeNode classes which are displayed.
   DirEnt rootTypeDirEnt;
   DirEnt rootLayerDirEnt;

   TypeTreeSelectionListener typeListener;
   TypeTreeSelectionListener layerListener;

   TreeEnt typeEmptyCommentNode, layerEmptyCommentNode;

   // Rules controlling when to refresh.  
   codeTypes =: refresh();
   codeFunctions =: refresh();

   // When the current type in the model changes, if we're in create mode we need to refresh to reflect the newly visible/highlighted elements.
   editorModel =: createMode || layerMode ? refresh() : null;

   enum EntType {
      Root,
      Comment,
      LayerGroup,
      LayerDir,
      LayerFile,
      InactiveLayer, // Layer in the index of all layers - one which has not yet been adding to the system
      ParentType,    // a class with children
      Type,          // a leaf class/object
      ParentObject,
      Object,
      ParentEnum,
      Enum,
      ParentEnumConstant,
      EnumConstant,
      ParentInterface,
      Interface,
      Package,
      Primitive;
   }

   @sc.obj.Sync(onDemand=true)
   class TreeEnt implements Comparable<TreeEnt>, sc.obj.IObjectId {
      EntType type;
      String value;
      String srcTypeName;
      Layer layer;
      boolean imported;
      boolean hasSrc;
      boolean transparent; // Entry does not exist on the file system yet
      boolean isTypeTree; // Or the layer tree
      boolean prependPackage; // Is this a type in the type tree or a file like web.xml which does not use a type name
      String objectId;

      TreeEnt(EntType type, String value, boolean isTypeTree, String srcTypeName, Layer layer) {
         this.type = type;
         this.value = value;
         this.isTypeTree = isTypeTree;
         this.srcTypeName = srcTypeName;
         this.layer = layer;
      }

      ArrayList<CodeType> entCodeTypes; // Which types and functions is this ent visible?
      ArrayList<CodeFunction> entCodeFunctions;

      // The value of getTypeDeclaration, once it's been fetched
      Object cachedTypeDeclaration;

      cachedTypeDeclaration =: typeAvailable();

      // Set to true when you need the type fetched
      boolean needsType = false;

      boolean selected = false;

      boolean closed = false; // Have we explicitly closed this node.  if so, don't reopen it

      boolean createModeSelected = false;

      UIIcon icon;

      String toString() {
         return value;
      }

      void toggleOpen() {
         // Root element is always open
         if (type == EntType.Root)
            return;
         if (!open) 
            open = true;
         else {
            open = false;
            closed = true; // Track when we explicit close it and then don't re-open it again
         }
         selectType(false);
      }

      private boolean open = false;

      void setOpen(boolean newOpen) {
         boolean orig = open;
         open = newOpen;
         if (!orig && newOpen) {
            initChildren();
         }
      }

      public void initChildren() {
         // subDirs is marked @Sync(onDemand=true) so it's left out of the sync when the
         // parent object is synchronized.  When a user opens the node, the startSync call begins
         // synchronizing this property.  On the client this causes a fetch of the data.
         // On the server, it pushes this property to the client on the next sync.
         SyncManager.startSync(this, "subDirs");
         SyncManager.startSync(this, "entries");
      }

      boolean getOpen() {
         return open;
      }

      void selectType(boolean append) {
         if (isTypeTree) {
            if (typeListener != null)
               typeListener.selectTreeEnt(this, append);
         }
         else {
            if (layerListener != null)
               layerListener.selectTreeEnt(this, append);
         }
      }

      void typeAvailable() {
         if (cachedTypeDeclaration == null)
            return;
         if (isTypeTree) {
            if (typeListener != null)
               typeListener.treeTypeAvailable(this);
         }
         else {
            if (layerListener != null)
               layerListener.treeTypeAvailable(this);
         }
      }

      String getTypeName() {
         String typeName = srcTypeName;
         //if (layer != null)
         //   typeName = TypeUtil.prefixPath(layer.packagePrefix, srcTypeName);

         return typeName;
      }

      String getPackageName() {
         Object type = getTypeDeclaration();
         if (type != null)
            return ModelUtil.getPackageName(type);
         return null;
      }

      boolean isSelectable() {
         return type != EntType.LayerGroup && type != EntType.Root;
      }

      // Just for childless tree nodes.  The dir does not inherit this method
      boolean hasAVisibleChild(boolean byLayer) {
         switch (type) {
            case Root:
               return true; // should the root always be visible?
         }
         return false;
      }

      int compareTo(TreeEnt c) {
         return value.compareTo(c.value);
      }

      boolean hasChild(String value) {
         return false;
      }

      boolean getHasChildren() {
         return false;
      }

      boolean getNeedsOpenClose() {
         return type != EntType.Root && hasChildren;
      }

      public int getNumChildren() {
         return 0;
      }

      public boolean isVisible(boolean byLayer) {
          // Always keep the selected guys visible
          if (editorModel.isTypeNameSelected(getTypeName()))
             return true;

          // This is a per-layer type so only show it if the layer matches
          if (layer != null && !layer.matchesFilter(codeTypes, codeFunctions))
             return false;

          if (srcTypeName != null) {
             switch (type) {
                case Root:
                case Comment:
                   return true;
                case Package:
                case LayerGroup: // A folder that includes layer directories
                   if (!hasAVisibleChild(byLayer))
                      return false;
                   return getTypeIsVisible();

                case InactiveLayer:
                   // Can't add a layer twice - only show inactive layers which are really not active
                   return layerMode && system.getLayerByDirName(value) == null;

                case LayerDir:   // A layer directory itself
                   if (addLayerMode) // the layer filters were applied above.  When adding layers though, don't show layers that are already there
                      return false;
                   return getTypeIsVisible();

                case LayerFile:
                   if (createMode) {
                      // In the type tree, we display the layer file.  In the layer tree, We display the layerDir as the layer in layer mode
                      if (layerMode)
                         return !byLayer && !addLayerMode;
                      else
                         return false;
                   }
                   return getTypeIsVisible();

                case ParentObject:
                   if (layerMode)
                      return false;
                   if (hasAVisibleChild(byLayer))
                      return true;
                case Object:
                   if (createMode || layerMode)
                       return false;
                   // FALL THROUGH to do type processing in app type mode.
                case ParentType:
                case ParentEnum:
                case ParentEnumConstant:
                case ParentInterface:
                   if (layerMode)
                      return false;

                   if (hasAVisibleChild(byLayer))
                      return true;
                case Interface:
                case Enum:
                case EnumConstant:
                case Type:
                   if (layerMode)
                      return false;

                   if (!getTypeIsVisible())
                      return false;

                   // Create mode:
                   //    show imported types or those which are in the current layer, or those imported which are defined in this layer
                   // Application mode:
                   //    just make sure we have the src for the type.  We'll have already doen the application check above.
                   //    if it's a transparent item, even if hasSrc is false we display it.
                   return (createMode && (imported || editorModel.currentLayer == null || layer == editorModel.currentLayer)) || hasSrc || transparent;

                case Primitive:
                   return createMode && propertyMode;
             }
          }
          return true;
      }

      Object getTypeDeclaration() {
         if (cachedTypeDeclaration != null)
            return cachedTypeDeclaration;
         if (typeName == null)
            return null;
         return DynUtil.resolveName(typeName, false);
      }

      boolean getTypeIsVisible() {
         if (entCodeTypes != null && codeTypes != null) {
            boolean vis = false;
            for (int i = 0; i < entCodeTypes.size(); i++) {
               if (codeTypes.contains(entCodeTypes.get(i))) {
                  vis = true;
                  break;
               }
            }
            if (!vis)
               return false;
         }
         if (entCodeFunctions != null && codeFunctions != null) {
            boolean vis = false;
            for (int i = 0; i < entCodeFunctions.size(); i++) {
               if (codeFunctions.contains(entCodeFunctions.get(i))) {
                  vis = true;
                  break;
               }
            }
            if (!vis)
               return false;
         }
         return true;
      }

      void findTypeTreeEnts(List<TreeEnt> res, String typeName) {
         if (this.typeName != null && this.typeName.equals(typeName)) {
            res.add(this);
         }
      }

      public void updateSelected() {
         if (typeName == null) {
            // Once there's a current type, all directories are deselected
            if (editorModel.typeNames.length > 0)
               selected = false;
            // Leave the folder selected as long as it marks the current package
            else if (!DynUtil.equalObjects(srcTypeName, editorModel.currentPackage))
               selected = false;
            return;
         }
         boolean newSel = editorModel.isTypeNameSelected(typeName);
         if (newSel != selected)
            selected = newSel;
         boolean newCreate = editorModel.isCreateModeTypeNameSelected(typeName);
         if (newCreate != createModeSelected)
            createModeSelected = newCreate;
         if (needsOpen() && !open && !closed)
            open = true;
      }

      boolean needsOpen() {
          return editorModel.createMode ? createModeSelected : selected;
      }

      @Constant
      String getObjectId() {
         if (objectId != null)
            return objectId;
         if (type == null || value == null)
            return null;
         String valuePart = CTypeUtil.escapeIdentifierString(srcTypeName == null ? (value == null ? "" : "_" + value.toString()) : "_" + srcTypeName);
         String typePart = type == null ? "_unknown" : "_" + type;
         String layerPart = layer == null ? "" : "_" + CTypeUtil.escapeIdentifierString(layer.layerName);
         objectId = (this instanceof DirEnt ? "DE" : "TE") + (isTypeTree ? "T" : "L") + typePart + layerPart + valuePart;
         return objectId;
      }
   }

   @sc.obj.Sync(onDemand=true)
   class DirEnt extends TreeEnt {
      @sc.obj.Sync(onDemand=true)
      LinkedHashMap<String,DirEnt> subDirs;
      @sc.obj.Sync(onDemand=true)
      ArrayList<TreeEnt> entries;
      ArrayList<TreeEnt> removed = null;

      DirEnt(EntType type, String value, boolean isTypeTree, String srcTypeName, Layer layer) {
         super(type, value, isTypeTree, srcTypeName, layer);
      }

      boolean hasVisibleChildren;

      subDirs =: refresh();

      void removeEntry(TreeEnt toRem) {
         if (removed == null) {
            removed = new ArrayList<TreeEnt>(1);
         }
         removed.add(toRem);
      }

      boolean hasChild(String value) {
         for (TreeEnt childEnt:entries) {
            if (childEnt.value.equals(value))
               return true;
         }
         return false;
      }

      boolean getHasChildren() {
         return true;
      }

      public int getNumChildren() {
         return (entries == null ? 0 : entries.size()) + (subDirs == null ? 0 : subDirs.size());
      }

      public List<TreeEnt> getChildren() {
         ArrayList<TreeEnt> children = new ArrayList<TreeEnt>();
         if (subDirs != null)
            children.addAll(subDirs.values());
         if (entries != null)
            children.addAll(entries);
         return children;
      }

      boolean hasAVisibleChild(boolean byLayer) {
         // Not yet fetched so we need to assume there is something visible here
         if (subDirs == null || entries == null)
             return hasVisibleChildren;
         for (DirEnt childEnt:subDirs.values()) {
             if (childEnt.isVisible(byLayer) || childEnt.hasAVisibleChild(byLayer))
                return hasVisibleChildren = true;
         }
         // Find all of the sub-dirs which have sub-types for them
         for (TreeEnt childEnt:entries) {
            if (childEnt.isVisible(byLayer) || childEnt.hasAVisibleChild(byLayer))
               return hasVisibleChildren = true;
         }
         return hasVisibleChildren = false;
      }

      void findTypeTreeEnts(List<TreeEnt> res, String typeName) {
         super.findTypeTreeEnts(res, typeName);
         if (subDirs != null) {
            for (DirEnt childEnt:subDirs.values()) {
               childEnt.findTypeTreeEnts(res, typeName);
            }
         }
         if (entries != null) {
            for (TreeEnt childEnt:entries) {
               childEnt.findTypeTreeEnts(res, typeName);
            }
         }
      }

      void updateSelected() {
         super.updateSelected();
         if (subDirs != null) {
            for (DirEnt childEnt:subDirs.values()) {
               childEnt.updateSelected();
               // auto-open trees when child nodes are selected
               if (!open && childEnt.needsOpen())
                   open = true;
            }
         }
         if (entries != null) {
            for (TreeEnt childEnt:entries) {
               childEnt.updateSelected();
               if (!open && childEnt.needsOpen())
                   open = true;
            }
         }
      }

      boolean needsOpen() {
         if (super.needsOpen())
            return true;
         // auto-open trees when child nodes are selected
         if (subDirs != null) {
            for (DirEnt childEnt:subDirs.values()) {
               if (childEnt.needsOpen())
                  return true;
            }
         }

         if (entries != null) {
            for (TreeEnt childEnt:entries) {
               if (childEnt.needsOpen())
                  return true;
            }
         }
         return false;
      }
   }

   String getTypeRootName() {
      String rootName;

      if (createMode) {
         if (propertyMode)
            rootName = "Select Property Type";
         else if (addLayerMode)
            rootName = "Select Layer to Include";
         else if (createLayerMode)
            rootName = "Select Extends Layers";
         else
            rootName = "Select Extends Type";
      }
      else
         rootName = "Application Types";

      return rootName;
   }

   String getLayerRootName() {
      String rootName; 
      if (createMode) {
         if (propertyMode)
            rootName = "Select Property Type by Layer";
         else if (addLayerMode)
            rootName = "Select Layer to Include by File";
         else if (createLayerMode)
            rootName = "Select Extends Layers by File";
         else
            rootName = "Select Extends Type by Layer";
      }
      else
         rootName = "Application Types by Layer";
      return rootName;
   }

   void selectionChanged() {
      editorModel.selectionChanged++;
      refresh();
   }

   void refresh() {
      // IF we have an empty tree during initialization it resets the "open" state for the startup node
      if (rebuildFirstTime) {
         valid = false;
         rebuild();
         rebuildFirstTime = false;
         return;
      }
      if (valid) {
         valid = false;

         scheduleBuild();
      }

   }

   // On the client, this will run after a 0 millisecond timeout.  
   // On the server, this runs at the end of the request.
   void scheduleBuild() {
      DynUtil.invokeLater(new Runnable() {
         public void run() {
            rebuild();
         }
      }, 9);
   }

   void rebuild() {
      if (refreshInProgress || valid)
         return;

      refreshInProgress = true;
      valid = true;

      try {
         refreshTypeTree();
         refreshLayerTree();
      }
      catch (RuntimeException exc) {
         System.err.println("*** error refreshing tree model: " + exc.toString());
         exc.printStackTrace();
      }
      finally {
         refreshInProgress = false;
      }
   }

   void refreshTypeTree() {
      if (codeTypes == null || codeFunctions == null)
         return;

      if (rootTypeDirEnt == null) {
         if (!rebuildTypeDirEnts())
            return;
      }

      typeTreeBuilt = true;
   }

   void refreshLayerTree() {
      if (codeTypes == null || codeFunctions == null)
         return;

      if (rootLayerDirEnt == null) {
         if (!rebuildLayerDirEnts())
            return;
      }

      layerTreeBuilt = true;
   }

   // On the client we can't rebuild these - they get populated from the server on a sync.
   boolean rebuildTypeDirEnts() {
      return false;
   }

   boolean rebuildLayerDirEnts() {
      return false;
   }
}
Here's the server specific layer of EditorModel. It responds to changes to some of its properties and sets others to create a dialog with the client.
file: editor/modelImpl/EditorModel.sc
import java.lang.reflect.Field;

import sc.type.IBeanMapper;
import java.util.TreeSet;

import sc.layer.LayeredSystem;

import sc.lang.java.TypeDeclaration;
import sc.lang.java.JavaSemanticNode;
import sc.lang.java.BodyTypeDeclaration;
import sc.lang.java.DeclarationType;
import sc.lang.java.InterfaceDeclaration;
import sc.lang.java.VariableDefinition;
import sc.lang.java.ModelUtil;
import sc.lang.sc.PropertyAssignment;

import sc.lang.IUndoOp;

import sc.parser.ParseUtil;

@sc.obj.Component
EditorModel {
   /** Among the typeNames, set to the type object which has focus */
   Object currentCtxType :=: ctx.currentType;

   currentCtxType =: changeCurrentType(currentCtxType);

   currentProperty =: validateCurrentProperty();

   system = LayeredSystem.getCurrent();

   typeNames =: invalidateModel();
   currentLayer =: invalidateModel();
   mergeLayers =: invalidateModel();
   inherit =: invalidateModel();

   codeFunctions =: invalidateModel();

   /** Set this to true so the command line interpreter and UI do not share the same current type, etc. */
   boolean separateContext = false;

   private EditorContext theSeparateCtx = null;
   private EditorContext getTheSeparateContext() {
      if (theSeparateCtx == null)
         theSeparateCtx = new EditorContext(system);
      return theSeparateCtx;
   }
   ctx := separateContext ? getTheSeparateContext() : system.getDefaultEditorContext();

   boolean modelsValid = true; // start out true so the first invalidate kicks in.... when nothing is selected, we are valid

   importedPropertyType := ctx.getImportedPropertyType(currentProperty);

   currentProperty =: currentPropertyIcon = GlobalResources.lookupUIIcon(currentProperty);


   // When the currentTypeSearch field is changed, this will look for a type matching that pattern, and if found change the current type.  this gets pushed to the client.
   currentTypeSearch =: findCurrentType(currentTypeSearch);

   void invalidateModel() {  // OVERRIDE in your framework to so rebuildModel is run in a doLater
      if (modelValidating) {
          System.out.println("*** Triggered model invalidate when the model was in the midst of validating!");
       }

      if (modelsValid) {
         modelsValid = false;

         DynUtil.invokeLater(new Runnable() {
            public void run() {
               rebuildModel();
               }}, 0);
      }
   }

   boolean modelValidating = false;

   void rebuildModel() {
      if (modelsValid)
         return;

      modelValidating = true;

      if (!triggeredByUndo) {
         boolean typesChanged = !StringUtil.arraysEqual(oldTypeNames,typeNames);

         if (oldTypeNames != null && typesChanged) {
            ctx.addOp(new IUndoOp() {
               String[] prevTypeNames = oldTypeNames;
               String[] newTypeNames = typeNames;

               void undo() {
                  triggeredByUndo = true;
                  typeNames = prevTypeNames;
                  oldTypeNames = newTypeNames;
               }
               void redo() {
                  triggeredByUndo = true;
                  oldTypeNames = prevTypeNames;
                  typeNames = newTypeNames;
               }

            });
         }
      }
      else
         triggeredByUndo = false;

      oldTypeNames = typeNames;

      ArrayList<Layer> newFilteredLayers = new ArrayList<Layer>();
      ArrayList<Layer> newTypeLayers = new ArrayList<Layer>();
      types = new ArrayList<Object>();
      inheritedTypes = new ArrayList<Object>();
      typesPerLayer = new ArrayList<List<Object>>();
      filteredTypes = new ArrayList<Object>();
      filteredTypesByLayer = new LinkedHashMap<String,List<Object>>();

      selectedFileIndex = new LinkedHashMap<String, SelectedFile>();
      selectedFileList = new ArrayList<SelectedFile>();

      for (String typeName:typeNames) {
         boolean isLayerType = false;

         Object type = system.getTypeDeclaration(typeName);
         if (type == null) {
            Layer layer = system.getLayerByTypeName(typeName);
            if (layer == null) {
               System.err.println("*** Can't find type or layer named: " + typeName);
               continue;
            }
            type = layer.model.getModelTypeDeclaration();
            isLayerType = true;
         }

         // Don't try to preserve the current layer when it goes from visible to invisible
         if (currentLayer != null && !currentLayer.matchesFilter(codeTypes, codeFunctions))
            currentLayer = null;

         // Pick the first visible layer in the type... if none are visible, then skip this type
         if (currentLayer == null) {
            if (type instanceof BodyTypeDeclaration) {
               BodyTypeDeclaration btd = (BodyTypeDeclaration) type;
               Layer typeLayer = btd.layer;
               while (typeLayer != null && !typeLayer.matchesFilter(codeTypes, codeFunctions)) {
                  btd = btd.getModifiedType();
                  if (btd == null)
                     break;
                  typeLayer = btd.layer;
               }
               // There is no version of this type in the selected layer
               if (btd == null)
                  continue;
               type = btd;
            }
         }

         Layer typeLayer = type instanceof BodyTypeDeclaration ? ((BodyTypeDeclaration) type).getLayer() : null;

         addLayerType(type, typeLayer, newFilteredLayers, newTypeLayers);

         if (typeLayer != null) {
            List<Layer> transLayers = typeLayer.getTransparentLayers();
            if (transLayers != null) {
               for (int i = 0; i < transLayers.size(); i++) {
                  addLayerType(type, transLayers.get(i), newFilteredLayers, newTypeLayers);
               }
            }
         }

         // Add this root type to the global list of types
         types.add(type);
         inheritedTypes.add(type);

         if (type instanceof TypeDeclaration) {
            TypeDeclaration rootTD = (TypeDeclaration) type;
            BodyTypeDeclaration modType = rootTD.getModifiedType();
            while (modType != null) {
               addLayerType(modType, modType.getLayer(), newFilteredLayers, newTypeLayers);
               modType = modType.getModifiedType();
            }
            if (inherit) {
               Object extType = rootTD.getExtendsTypeDeclaration();
               while (extType != null && extType instanceof BodyTypeDeclaration) {
                  BodyTypeDeclaration eTD = (BodyTypeDeclaration) extType;
                  // Use this method to just add the layer to the layer indexes.  When type is null, no type is added.
                  addLayerType(eTD, eTD.getLayer(), newFilteredLayers, newTypeLayers);

                  BodyTypeDeclaration eTDRoot = eTD.getModifiedByRoot();

                  if (!inheritedTypes.contains(eTDRoot))
                     inheritedTypes.add(eTDRoot);

                  BodyTypeDeclaration extModType;
                  BodyTypeDeclaration mtype = eTD;
                  while ((extModType = mtype.getModifiedType()) != null) {
                     addLayerType(extModType, extModType.getLayer(), newFilteredLayers, newTypeLayers);
                     mtype = extModType;
                  }
                  extType = eTD.getExtendsTypeDeclaration();
               }

               addInterfaceLayerTypes(rootTD, newFilteredLayers, newTypeLayers, inheritedTypes);
            }
         }
      }

      filteredTypeLayers = newFilteredLayers;
      // Make sure this does not change when just currentLayer changes...
      if (typeLayers == null || !typeLayers.equals(newTypeLayers))
         typeLayers = newTypeLayers;

      boolean resetCurrentLayer = true;
      if (typeLayers != null) {
         if (currentLayer != null) {
            if (typeLayers.contains(currentLayer))
               resetCurrentLayer = false;
         }
         if (resetCurrentLayer && typeLayers.size() > 0)
            currentLayer = typeLayers.get(typeLayers.size()-1);
      }

      if (filteredTypes.size() > 0)
         currentType = filteredTypes.get(0);
      else
         currentType = null;

      // Clear out any selected property.
      currentProperty = null;
      currentPropertyType = currentType;
      savedPropertyValue = currentPropertyValue = null;
      savedPropertyOperator = currentPropertyOperator = null;
      currentInstance = null;

      if (currentType != null) {
         currentTypeIsLayer = ModelUtil.isLayerType(currentType);
         if (currentTypeIsLayer)
            currentPackage = currentLayer.packagePrefix;
         else
            currentPackage = ModelUtil.getPackageName(currentType);
      }
      else {
         currentPackage = "";
         currentTypeIsLayer = false;
      }

      if (currentCtxType != currentType)
         currentCtxType = currentType;

      updateCurrentJavaModel();

      ArrayList newVisibleTypes = new ArrayList();
      if (types != null) {
         for (Object type:types) {
            type = processVisibleType(type);
            if (type != null) {
               newVisibleTypes.add(type);
            }
         }
      }
      visibleTypes = newVisibleTypes;

      // Do this at the end in case any of our changes trigger the model
      modelsValid = true;

      modelValidating = false;


      // Send an event so people can listen on this value and update dependent data structures
      Bind.sendEvent(sc.bind.IListener.VALUE_CHANGED, this, null);
   }

   private void addInterfaceLayerTypes(BodyTypeDeclaration rootTD, ArrayList<Layer> newFilteredLayers, ArrayList<Layer> newTypeLayers, List<Object> inheritedTypes) {
      Object[] implTypes = rootTD.getImplementsTypeDeclarations();
      if (implTypes != null) {
         for (Object implTypeObj:implTypes) {
            if (implTypeObj instanceof TypeDeclaration) {
               TypeDeclaration implType = (TypeDeclaration) implTypeObj;

               addLayerType(implType, implType.getLayer(), newFilteredLayers, newTypeLayers);

               BodyTypeDeclaration iTDRoot = implType.getModifiedByRoot();

               if (!inheritedTypes.contains(iTDRoot))
                  inheritedTypes.add(iTDRoot);

               BodyTypeDeclaration extModType;
               BodyTypeDeclaration mtype = implType;
               while ((extModType = mtype.getModifiedType()) != null) {
                  addLayerType(extModType, extModType.getLayer(), newFilteredLayers, newTypeLayers);
                  mtype = extModType;
               }

               addInterfaceLayerTypes(implType, newFilteredLayers, newTypeLayers, inheritedTypes);
            }
         }
      }
   }

   private void addLayerType(Object type, Layer layer, ArrayList<Layer> newFilteredLayers, ArrayList<Layer> newTypeLayers) {
      Layer prevLayer;
      if (type instanceof TypeDeclaration) {
         TypeDeclaration td = (TypeDeclaration) type;

         ParseUtil.initAndStartComponent(td);

         BodyTypeDeclaration prevType = td.getModifiedByType();
         prevLayer = prevType == null ? null : prevType.getLayer();
      }
      else {
         prevLayer = null;
      }

      boolean isTypeLayer = true;

      // When we filter a layer, we remove it from the allLayers attribute as well as not showing any types from it.
      if (layer != null && !layer.matchesFilter(codeTypes, codeFunctions))
         return;

      // Don't show this layer if we have a currentLayer set and depending on the "mergeLayers" flag we should or not
      if (layer != null && currentLayer != null && ((!mergeLayers && currentLayer != layer) || (mergeLayers && currentLayer.getLayerPosition() < layer.getLayerPosition()))) {
         isTypeLayer = false;
      }

      if (isTypeLayer) {
         int layerIx = newFilteredLayers.indexOf(layer);
         List<Object> layerTypes;
         if (layerIx == -1) {
            layerIx = newFilteredLayers.size();
            // Keep layers sorted with null as the very first layer if it is present
            int pos;

            if (layer == null) {
               pos = 0;
            }
            else {
               for (pos = 0; pos < newFilteredLayers.size(); pos++) {
                  Layer cur = newFilteredLayers.get(pos);
                  if (cur != null && cur.layerPosition > layer.layerPosition)
                     break;
               }
            }
            newFilteredLayers.add(pos, layer);
            typesPerLayer.add(pos, layerTypes = new ArrayList<Object>());
         }
         else
            layerTypes = typesPerLayer.get(layerIx);

         // Also associate ths type with its layer
         if (type != null) {
            layerTypes.add(type);

            String typeName = ModelUtil.getTypeName(type);
            List<Object> typeList = filteredTypesByLayer.get(typeName);
            if (typeList == null) {
               typeList = new ArrayList<Object>();
               filteredTypesByLayer.put(typeName, typeList);
            }
            typeList.add(type);

            // If the previous type is null or the previous type's position is outside the selected region, do not include it
            // Otherwise, this type was already added to the global list in a previous layer's type
            if (prevLayer == null || (currentLayer != null && prevLayer.getLayerPosition() > currentLayer.getLayerPosition()))
               filteredTypes.add(type);

            if (type instanceof BodyTypeDeclaration) {
               BodyTypeDeclaration btd = (BodyTypeDeclaration) type;
               JavaModel javaModel = btd.getJavaModel();
               SrcEntry ent = javaModel.getSrcFile();
               if (ent != null) {
                  SelectedFile f = selectedFileIndex.get(ent.absFileName);
                  if (f == null) {
                     f = new SelectedFile();
                     f.file = ent;
                     f.types = new ArrayList<Object>();
                     f.model = javaModel;
                     f.layer = currentLayer;
                     selectedFileIndex.put(ent.absFileName, f);
                     selectedFileList.add(f);
                  }
                  if (!f.types.contains(type))
                     f.types.add(type);
               }
            }
         }
      } 
      int allIx = newTypeLayers.indexOf(layer);
      if (allIx == -1) {
         int i;
         for (i = 0; i < newTypeLayers.size(); i++) {
            if (newTypeLayers.get(i).layerPosition < layer.layerPosition) {
               newTypeLayers.add(i, layer);
               break;
            }
         }
         if (i == newTypeLayers.size())
            newTypeLayers.add(layer);
      }
   }

   private static HashSet<String> filteredProps = new HashSet<String>();
   static {
      filteredProps.add("class");
      filteredProps.add("initState");
   }

   public boolean filteredProperty(Object type, Object p, boolean perLayer) {
      // For now, only StrataCode members
      if (p instanceof java.lang.reflect.Member)
         return true;

      if (p instanceof IBeanMapper && ((IBeanMapper) p).getPropertyMember() instanceof java.lang.reflect.Member)
         return true;

      Object ownerType = ModelUtil.getEnclosingType(p);

      if (type instanceof ClientTypeDeclaration)
         type = ((ClientTypeDeclaration) type).getOriginal();

      // Normally !inherit mode only uses the declared properties.  But for transparent layers we have to get all of them and filter them here
      if (!inherit && !ModelUtil.sameTypes(ownerType, type))
         return true;

      // In threeD view, we don't want to merge the properties as we go up the layer stack unlike form view.
      if (perLayer && ModelUtil.getLayerForType(null, type) != ModelUtil.getLayerForType(null, ownerType))
         return true;

      String pname;
      return p == null || (pname = ModelUtil.getPropertyName(p)).startsWith("_") || filteredProps.contains(pname);
   }

   /** When merging layers we use extendsLayer so that we do not pick up independent layers which which just happen to sit lower in the stack, below the selected layer */
   public boolean currentLayerMatches(Layer layer) {
      if (currentLayer == null)
         return true;
      if (currentLayer.transparentToLayer(layer))
         return true;
      return ((!mergeLayers && currentLayer == layer) || (mergeLayers && (layer == currentLayer || currentLayer.extendsLayer(layer))));
   }

   public Object[] getPropertiesForType(Object type) {
      if (type instanceof ClientTypeDeclaration)
         type = ((ClientTypeDeclaration) type).getOriginal();
      Object[] props;
      if (!mergeLayers) {
         // Transparent layers need to grab all of the properties so we can filter them in the code
         if (!inherit && (currentLayer == null || !currentLayer.transparent))
            props = ModelUtil.getDeclaredPropertiesAndTypes(type, null);
          else
            props = ModelUtil.getPropertiesAndTypes(type, null);
      }
      else {
         if (!inherit && (currentLayer == null || !currentLayer.transparent))
            props = ModelUtil.getDeclaredMergedPropertiesAndTypes(type, null, true);
         else
            props = ModelUtil.getMergedPropertiesAndTypes(type, null);
      }
      return props;
   }

   public Object[] toClientTypeDeclarations(Object[] types) {
      if (types == null)
         return null;
      int i = 0;
      for (Object type:types) {
         if (type instanceof ClientTypeDeclaration)
            continue;
         if (type instanceof BodyTypeDeclaration)
            types[i] = ((BodyTypeDeclaration) type).getClientTypeDeclaration();
      }
      return types;
   }

   String setElementValue(Object type, Object inst, Object prop, String expr, boolean updateInstances, boolean valueIsExpr) {
      if (type instanceof ClientTypeDeclaration)
         type = ((ClientTypeDeclaration) type).getOriginal();
      // The first time they are changing the object in a transparent layer.  We need to create it in this case.
      if (currentLayer != null && currentLayer.transparent && ModelUtil.getLayerForType(null, type) != currentLayer) {
         String typeName = ModelUtil.getTypeName(type);
         type = ctx.addTopLevelType(null, CTypeUtil.getPackageName(typeName), currentLayer, CTypeUtil.getClassName(typeName), null);
         invalidateModel();
      }
      return ctx.setElementValue(type, inst, prop, expr, updateInstances, valueIsExpr);
   }

   String updateCurrentProperty(Object operator, String value) {
      return setElementValue(currentPropertyType, null, currentProperty, operator + value, true, true);
   }

   void validateCurrentProperty() {
      Object prop = currentProperty;
      if (prop == null) {
         currentPropertyType = null;
      }
      else {
         currentPropertyType = ModelUtil.getEnclosingType(prop);
         savedPropertyValue = currentPropertyValue = ctx.propertyValueString(currentType, null, prop);
         savedPropertyOperator = currentPropertyOperator = ModelUtil.getOperator(currentProperty);
         if (savedPropertyOperator == null)
            savedPropertyOperator = currentPropertyOperator = "=";
      }
      propertySelectionChanged();
   }

   // Called when the current JavaModel changes
   private object modelEventListener extends AbstractListener {
      public boolean valueValidated(Object obj, Object prop, Object eventDetail, boolean apply) {
         if (currentType != null && currentProperty != null) {
            savedPropertyValue = currentPropertyValue = ctx.propertyValueString(currentType, null, currentProperty);
            savedPropertyOperator = currentPropertyOperator = ModelUtil.getOperator(currentProperty);
            if (savedPropertyOperator == null)
               savedPropertyOperator = currentPropertyOperator = "=";
         }

         // If the model has changed, the type itself may have changed
         if (currentType instanceof BodyTypeDeclaration) {
            BodyTypeDeclaration typeDecl = (BodyTypeDeclaration) currentType;

            BodyTypeDeclaration newTypeDecl = typeDecl.resolve(false);
            // When the type has changed, update the current model which will trigger the rebuilding of the form
            if (newTypeDecl != typeDecl) {
               currentType = newTypeDecl;

               invalidateModel();

/*
               int i = 0;
               for (Object type:types) {
                  if (type instanceof BodyTypeDeclaration)
                     types.set(i, ((BodyTypeDeclaration) type).resolve(false));
                  i++;
               }
*/
            }
         }

         return true;
      }
   }

   void removeCurrentListener() {
      if (currentJavaModel != null) {
         Bind.removeListener(currentJavaModel, null, modelEventListener, IListener.VALUE_CHANGED);
      }
   }

   void updateCurrentJavaModel() {
      JavaModel newModel = ModelUtil.getJavaModel(currentType);
      if (newModel != currentJavaModel) {
         removeCurrentListener();
         currentJavaModel = newModel;
         if (newModel != null) {
            Bind.addListener(currentJavaModel, null, modelEventListener, IListener.VALUE_CHANGED);
         }
      }
   }

   void changeCurrentType(Object type) {
      super.changeCurrentType(type);

      // Push this back if the change is coming from the editor model side
      if (currentCtxType != type)
         currentCtxType = type;

      updateCurrentJavaModel();
   }

   void clearCurrentType() {
      super.clearCurrentType();
      updateCurrentJavaModel();
   }

   String getPropertySelectionName() {
      if (currentProperty != null) {
         String dynPrefix = ModelUtil.isDynamicProperty(currentProperty) ? " Dynamic" : " Compiled";
         if (currentProperty instanceof VariableDefinition) {
            return dynPrefix + " Field";
         }
         else if (currentProperty instanceof PropertyAssignment) {
            return dynPrefix + " Property Assignment";
         }
         else if (currentProperty instanceof Field)
            return " Native Field";
         else
            return " ???"; // method?
      }
      else
         return null;
   }

   String getTypeSelectionName() {
      if (currentType != null) {
         DeclarationType declType = ModelUtil.getDeclarationType(currentType);
         String name = declType.name;
         String prefix;
         if (currentType instanceof BodyTypeDeclaration) {
            prefix = ModelUtil.isDynamicType(currentType) ? " Dynamic" : " Compiled";
         }
         else
            prefix = " Native";

         return " " + Character.toUpperCase(name.charAt(0)) + name.substring(1);
      }
      else
         return null;
   }

   public void deleteCurrentProperty() {
      if (currentType != null && currentProperty != null && currentType instanceof BodyTypeDeclaration && currentProperty instanceof JavaSemanticNode) {
         ctx.removeProperty((BodyTypeDeclaration) currentType, (JavaSemanticNode) currentProperty, true);
         clearCurrentProperty();
      }
      else
         System.err.println("*** Can't delete current property");
   }

   public void deleteCurrentLayer() {
      if (currentLayer != null) {
         ctx.removeLayer(currentLayer, true);
         clearCurrentType();
      }
      else
         System.err.println("*** no current layer to delete");
   }

   public void deleteCurrentType() {
      if (currentType != null || !(currentType instanceof BodyTypeDeclaration)) {
         ctx.removeType((BodyTypeDeclaration) currentType, true);
         clearCurrentType();
      }
      else
         System.err.println("*** no current type to delete");
   }

   public void deleteCurrentSelection() {
      if (currentProperty != null) {
         deleteCurrentProperty();
      }
      else if (currentTypeIsLayer) {
         deleteCurrentLayer();
      }
      else if (currentType != null) {
         deleteCurrentType();
      }
   }

   public String findCurrentType(String rootName) {
      if (rootName == null)
         return null;

      // First see if they specified the whole name
      BodyTypeDeclaration theType = system.getSrcTypeDeclaration(rootName, null, true);

      if (theType == null) {
         List<BodyTypeDeclaration> types = system.findTypesByRootName(rootName);
         if (types == null || types.size() == 0) {
            return "No types named: " + rootName;
         }
         theType = types.get(0);
      }
      changeCurrentType(theType);
      return null;
   }

   public void commitMemorySessionChanges() {
      ctx.commitMemorySessionChanges();
      invalidateModel();
   }

   void clearCurrentProperty() {
      currentProperty = null;
      currentPropertyType = null;
      currentPropertyValue = null;
      currentPropertyOperator = null;
      currentInstance = null;

      propertySelectionChanged();
   }

   void propertySelectionChanged() {
      // Need to manually change these properties when the current property changes cause rebuildModel does not send the "model changed" event in this case
      if (editSelectionEnabled != getEditableProperty())
         editSelectionEnabled = !editSelectionEnabled;
      Bind.sendDynamicEvent(IListener.VALUE_CHANGED, this, "currentSelectionName");;
   }

   public boolean getEditableProperty() {
      if (currentProperty != null) {
         if (currentProperty instanceof VariableDefinition) {
            return true;
         }
         else if (currentProperty instanceof PropertyAssignment) {
            return true;
         }
         else if (currentProperty instanceof Field)
            return false;
         else
            return false;
      }
      else if (currentTypeIsLayer) {
         return true;
      }
      else if (currentType != null) {
         return true;
      }
      else {
         return false;
      }
   }

   public String addTopLevelType(String mode, String currentPackage, Layer layer, String name, String extType) {
      Object res = ctx.addTopLevelType(mode, currentPackage, layer, name, extType);
      if (res instanceof String)
         return (String) res;

      changeCurrentType(res);

      return null;
   }

   void removeLayers(ArrayList<Layer> layers) {
      ctx.removeLayers(layers);
   }

   ClientTypeDeclaration toClientType(Object type) {
      if (type instanceof BodyTypeDeclaration) {
         if (type instanceof ClientTypeDeclaration)
            return (ClientTypeDeclaration) type;
         return ((BodyTypeDeclaration) type).getClientTypeDeclaration();
      }
      return null;
   }

   BodyTypeDeclaration processVisibleType(Object typeObj) {
      if (typeObj instanceof BodyTypeDeclaration) {
         return toClientType(((BodyTypeDeclaration) typeObj).getDeclarationForLayer(currentLayer, inherit, mergeLayers));
      }
      return null;
   }


   void stop() {
      removeCurrentListener();
   }
}
Here the server layer of TypeTreeModel:
file: editor/modelImpl/TypeTreeModel.sc
import sc.layer.LayeredSystem;
import sc.layer.IModelListener;
import sc.layer.LayerIndexInfo;

import sc.type.Type;

import sc.lang.java.BodyTypeDeclaration;
import sc.lang.java.ITypeDeclaration;
import sc.lang.java.TypeDeclaration;
import sc.lang.java.ModelUtil;
import sc.lang.java.JavaModel;

import sc.lang.ILanguageModel;

import java.util.Collections;

TypeTreeModel {
   ArrayList<String> packageFilter;


   // TODO: not implemented yet Populated from specifiedLayerNames.  Defines the layers from which we are doing source files.
   ArrayList<Layer> specifiedLayers;

   system = LayeredSystem.getCurrent();

   // TODO: fix this!  Ideally we do not load the types until you click on them.  At that point, we need to load all types, including those which extend the selected type.
   // It might be simpler to optimize this at the package level.  We'll load the inner types of all types when we initialize the type.  The obstacle now is that we need to
   // create DirEnt's for each type once it's been selected.  Maybe we use the addTypeToLayer and addModelType methods?  Essentially re-adding these types... that will add new entries to the subDirs for the parent and push those changes to the client.
   static boolean loadInnerTypesAtStartup = true;

   IModelListener listener;

   void start() {
      system.addNewModelListener(listener = new IModelListener() {
         void modelAdded(ILanguageModel m) {
            addNewModel(m);
         }
         void innerTypeAdded(ITypeDeclaration td) {
            addNewType(td);
         }
         void layerAdded(Layer l) {
            addNewLayer(l);
         }
         void modelRemoved(ILanguageModel m) {
            removeModel(m);
         }
         void innerTypeRemoved(ITypeDeclaration td) {
            removeType(td);
         }
         void layerRemoved(Layer l) {
            removeLayer(l);
         }
         void runtimeAdded(LayeredSystem sys) {
         }
      });
   }

   ArrayList<String> getExtendedPrimitiveTypeNames() {
      Set<String> primNames = Type.getPrimitiveTypeNames();
      ArrayList<String> res = new ArrayList<String>(primNames.size()+1);
      res.addAll(primNames);
      res.remove("void");
      res.add("String");
      return res;
   }


   // For testing use these to cut down the number of types or layers
   private static int MAX_TYPES = 20000; // 100;
   private static int MAX_LAYERS = 20000; // 10;

   boolean includeInactive = false;
   boolean includePrimitives = false;

   boolean isFilteredType(String typeName) {
      if (packageFilter == null)
         return false;
      if (typeName == null)
         return true;
      for (String pkg:packageFilter) {
         if (typeName.startsWith(pkg))
            return false;
      }
      return true;
   }

   boolean isFilteredPackage(String pkgName) {
      if (packageFilter == null)
         return false;
      if (pkgName == null)
         return false;
      for (String pkgFilter:packageFilter) {
         if (pkgName.startsWith(pkgFilter) || pkgFilter.startsWith(pkgName))
            return false;
      }
      return true;
   }

   boolean rebuildTypeDirEnts() {
      DirEnt rootEnts = new DirEnt(EntType.Root, "All Types", true, null, null);
      rootTypeDirEnt = rootEnts;

      if (includePrimitives) {
         // First add the primitive types
         for (String primTypeName:getExtendedPrimitiveTypeNames()) {
            TreeEnt ent = new TreeEnt(EntType.Primitive, primTypeName, true, primTypeName, null);
            ent.prependPackage = true;

            // Primitives are treated like imported types since they are not defined inside layers as src
            ent.imported = true;
            ent.hasSrc = false;
            rootEnts.entries.add(ent);
         }
      }

      TreeEnt cent = typeEmptyCommentNode = new TreeEnt(EntType.Comment, "No visible types", true, null, null);

      Set<String> srcTypeNames = system.getSrcTypeNames(true, loadInnerTypesAtStartup, false, true, true);
      if (specifiedLayerNames != null) {
         specifiedLayers = new ArrayList<Layer>(specifiedLayerNames.length);
         for (int i = 0; i < specifiedLayerNames.length; i++) {
            Layer layer = system.getInactiveLayer(specifiedLayerNames[i], true, true, true, false);
            if (layer == null)
               System.err.println("*** TypeTreeModel: Unable to find layer with specifiedLayerName: " + specifiedLayerNames[i]);
            else {
               // TODO: we should put these into dependency order but we can't use position cause these are inactive.
               specifiedLayers.add(layer);
            }
         }
         Set<String> additionalNames = system.getSrcTypeNames(specifiedLayers, true, loadInnerTypesAtStartup, false, true);
         if (additionalNames != null) {
            if (srcTypeNames == null)
               srcTypeNames = additionalNames;
            else
               srcTypeNames.addAll(additionalNames);
         }
      }

      // Then build our DirEnt structure from the Set of src type names we get back
      for (String srcTypeName:srcTypeNames) {
         if (!isFilteredType(srcTypeName)) {
            addModelToTypeTree(srcTypeName, true);
            if (++typesCreated >= MAX_TYPES) {
               System.out.println("*** Skipping some types due to max types setting of: " + MAX_TYPES);
               break;
            }
         }
      }

      // This retrieves all of the layer definitions in the system and registers them in the
      // type index.

      Map<String,LayerIndexInfo> allLayerIndex = system.getAllLayerIndex();
      for (LayerIndexInfo lii:allLayerIndex.values()) {
          if (!includeInactive)
             break;
         // Do not replace a system layer with one from the index
         //if (system.getLayerByDirName(lii.layerDirName) == null) {
            String pkg = lii.packageName;
            if (pkg == null)
               pkg = "<Global Layers>";

            DirEnt pkgEnts = lookupPackage(rootEnts.subDirs, pkg, EntType.Package, null, null, true, true);

            TreeEnt ent = new TreeEnt(EntType.InactiveLayer, lii.layerDirName, true, lii.layerDirName, null);
            ent.prependPackage = true;

            pkgEnts.entries.add(ent);
         //}
      }

      rootEnts.processEntry();

      return true;
   }

   TreeEnt addModelToTypeTree(String srcTypeName, boolean prependPackage) {
      String pkg = CTypeUtil.getPackageName(srcTypeName);
      String className = CTypeUtil.getClassName(srcTypeName);
      TreeEnt ent = new TreeEnt(EntType.Type, className, true, srcTypeName, null);
      ent.prependPackage = prependPackage;

      // If this name is defined in an import and we do not have a src file for it, set the imported flag.
      ent.imported = system.getImportDecl(null, null, className) != null;

      TypeDeclaration typeDecl = loadInnerTypesAtStartup ? system.getSrcTypeDeclaration(srcTypeName, null, true) : null;
      if (typeDecl == null && specifiedLayers != null) {
          System.err.println("*** specified layer names in type tree model not yet implemented");
          // TODO: need a new system.getInactiveTypeDeclaration method here.  Alternatively now thinking about just
          // creating a separate inactive layered system.   That way we can pull in all dependent layers, sort them etc. and do
          // the full type stuff on them.
      }
      if (typeDecl != null && typeDecl.isLayerType) {
          ent.type = EntType.LayerFile;
          ent.layer = typeDecl.getLayer();
      }

      if (typeDecl == null) {
         Layer layer = system.getLayerByTypeName(srcTypeName);
         if (layer != null) {
            ent.type = EntType.LayerFile;
            ent.layer = layer;
            ent.hasSrc = true;
         }
      }
      else {
         ent.hasSrc = true;
      }

      // When loadInnerTypesAtStartup is false assuming there is src so types are visible.
      ent.hasSrc = typeDecl != null || !loadInnerTypesAtStartup;
      if (pkg != null) {
         EntType pkgType = EntType.Package;
         if (typeDecl != null && typeDecl.getEnclosingType() != null)
            pkgType = EntType.ParentType;
         DirEnt pkgEnts = lookupPackage(rootTypeDirEnt.subDirs, pkg, pkgType, null, null, true, true);
         if (!pkgEnts.hasChild(ent.value))
            pkgEnts.entries.add(ent);
      }
      else {
         if (!rootTypeDirEnt.hasChild(ent.value))
            rootTypeDirEnt.entries.add(ent);
      }
      return ent;
   }

   TreeEnt getTypeTreeEnt(String srcTypeName) {
      String pkg = CTypeUtil.getPackageName(srcTypeName);
      String className = CTypeUtil.getClassName(srcTypeName);

      DirEnt pkgEnt;
      TreeEnt foundEnt = null;

      if (pkg != null)
         pkgEnt = lookupPackage(rootTypeDirEnt.subDirs, pkg, EntType.Package, null, null, false, true);
      else
         pkgEnt = rootTypeDirEnt;

      if (pkgEnt == null || pkgEnt.subDirs == null)
         return null;

      DirEnt childDir = pkgEnt.subDirs.get(className);
      if (childDir != null) {
         return childDir;
      }

      if (pkgEnt.entries != null) {
         for (int i = 0; i < pkgEnt.entries.size(); i++) {
            if (pkgEnt.entries.get(i).value.equals(className)) {
               foundEnt = pkgEnt.entries.get(i);
               break;
            }
         }
      }
      return foundEnt;
   }

   TreeEnt removeTypeFromTypeTree(String srcTypeName) {
      String pkg = CTypeUtil.getPackageName(srcTypeName);
      String className = CTypeUtil.getClassName(srcTypeName);

      DirEnt pkgEnt;
      TreeEnt foundEnt = null;

      if (pkg != null)
         pkgEnt = lookupPackage(rootTypeDirEnt.subDirs, pkg, EntType.Package, null, null, false, true);
      else
         pkgEnt = rootTypeDirEnt;

      if (pkgEnt == null || pkgEnt.subDirs == null)
         return null;

      DirEnt childDir = pkgEnt.subDirs.get(className);
      if (childDir != null) {
         pkgEnt.subDirs.remove(className);
         pkgEnt.removeEntry(childDir);
      }

      if (pkgEnt.entries != null) {
         for (int i = 0; i < pkgEnt.entries.size(); i++) {
            if (pkgEnt.entries.get(i).value.equals(className)) {
               foundEnt = pkgEnt.entries.get(i);

               pkgEnt.entries.remove(i);
               pkgEnt.removeEntry(foundEnt);

               break;
            }
         }
      }
      return foundEnt;
   }

   TreeEnt removeLayerFromTypeTree(Layer layer, boolean remove) {
      String srcTypeName = layer.getLayerModelTypeName();
      String pkg = CTypeUtil.getPackageName(srcTypeName);
      String className = CTypeUtil.getClassName(srcTypeName);

      DirEnt pkgEnt;
      TreeEnt foundEnt = null;

      if (pkg != null)
         pkgEnt = lookupPackage(rootTypeDirEnt.subDirs, pkg, EntType.Package, null, null, false, true);
      else
         pkgEnt = rootTypeDirEnt;

      if (pkgEnt == null)
         return null;

      for (int i = 0; i < pkgEnt.entries.size(); i++) {
         if (pkgEnt.entries.get(i).value.equals(className)) {
            foundEnt = pkgEnt.entries.get(i);

            if (remove) {
               pkgEnt.entries.remove(i);
               pkgEnt.removeEntry(foundEnt);
            }
            else {
               foundEnt.type = EntType.InactiveLayer;
               foundEnt.value = layer.layerDirName;
               foundEnt.srcTypeName = layer.layerDirName;
               if (foundEnt instanceof DirEnt) {
                  DirEnt de = (DirEnt) foundEnt;
                  de.subDirs.clear();
                  de.entries.clear();
               }
            }
            break;
         }
      }
      return foundEnt;
   }

   TreeEnt addModelToLayerTree(ILanguageModel m, boolean prependPackage) {
      Layer layer = m.layer;
      if (layer == null)
         return null;

      SrcEntry src = m.srcFile;

      String layerName = layer.layerName;

      String layerGroup = CTypeUtil.getPackageName(layerName);
      String layerFile = CTypeUtil.getClassName(layerName);
      DirEnt layerParentEnt = rootLayerDirEnt;
      if (layerGroup != null)
         layerParentEnt = lookupPackage(rootLayerDirEnt.subDirs, layerGroup, EntType.LayerGroup, null, null, true, false);

      DirEnt layerDirEnt = lookupPackage(layerParentEnt.subDirs, layerFile, EntType.LayerGroup, layer.packagePrefix, null, true, false);
      layerDirEnt.srcTypeName = "layerdir:" + layerDirEnt.srcTypeName; // Need to munch this so selecting the dir does not select the file

      String layerType = src.relTypeName;
      String fileDir = CTypeUtil.getPackageName(layerType);
      String fileTail = CTypeUtil.getClassName(layerType);
      TreeEnt ent = new TreeEnt(EntType.Type, fileTail, false, m.getModelTypeDeclaration().getFullTypeName(), layer);
      // If this entity is imported into this layer from outside, set the imported flag
      ent.imported = layer.getImportDecl(fileTail, false) != null;
      ent.prependPackage = prependPackage;
      Object td = ent.typeDeclaration;
      JavaModel jm = null;
      if (td != null)
         jm = ModelUtil.getJavaModel(td);
      ent.hasSrc = layer.findSrcFile(jm == null ? layerType : jm.getSrcFile().relFileName, true) != null;
      if (fileDir == null) {
         layerDirEnt.entries.add(ent);
      }
      else {
         DirEnt fileEnt = lookupPackage(layerDirEnt.subDirs, fileDir, EntType.Type, layer.packagePrefix, layer, true, false);
         fileEnt.entries.add(ent);
      }

      return ent;
   }

   TreeEnt addTypeToLayerTree(ITypeDeclaration itd) {
      if (!(itd instanceof BodyTypeDeclaration))
         return null;

      BodyTypeDeclaration td = (BodyTypeDeclaration) itd;
      Layer layer = td.getLayer();

      if (layer == null)
         return null;

      String layerName = layer.layerName;

      String layerGroup = CTypeUtil.getPackageName(layerName);
      String layerFile = CTypeUtil.getClassName(layerName);
      DirEnt layerParentEnt = rootLayerDirEnt;
      if (layerGroup != null)
         layerParentEnt = lookupPackage(rootLayerDirEnt.subDirs, layerGroup, EntType.LayerGroup, null, null, true, false);

      DirEnt layerDirEnt = lookupPackage(layerParentEnt.subDirs, layerFile, EntType.LayerGroup, layer.packagePrefix, null, true, false);
      layerDirEnt.srcTypeName = "layerdir:" + layerDirEnt.srcTypeName; // Need to munch this so selecting the dir does not select the file

      SrcEntry src = td.getJavaModel().getSrcFile();
      String layerType = CTypeUtil.prefixPath(src.getRelDir(), td.getInnerTypeName());
      String fileDir = CTypeUtil.getPackageName(layerType);
      String fileTail = CTypeUtil.getClassName(layerType);
      TreeEnt ent = new TreeEnt(EntType.Type, fileTail, false, td.getFullTypeName(), layer);
      ent.prependPackage = true;
      ent.layer = layer;
      // If this entity is imported into this layer from outside, set the imported flag
      ent.imported = layer.getImportDecl(fileTail, false) != null;
      ent.hasSrc = true;
      if (fileDir == null) {
         layerDirEnt.entries.add(ent);
      }
      else {
         DirEnt fileEnt = lookupPackage(layerDirEnt.subDirs, fileDir, EntType.Type, layer.packagePrefix, layer, true, false);
         fileEnt.entries.add(ent);
      }
      return ent;
   }

   TreeEnt removeModelFromLayerTree(ILanguageModel m) {
      Layer layer = m.layer;
      if (layer == null)
         return null;

      SrcEntry src = m.srcFile;

      String layerName = layer.layerName;
      String layerGroup = CTypeUtil.getPackageName(layerName);
      String layerFile = CTypeUtil.getClassName(layerName);
      DirEnt layerParentEnt = rootLayerDirEnt;
      if (layerGroup != null)
         layerParentEnt = lookupPackage(rootLayerDirEnt.subDirs, layerGroup, EntType.LayerGroup, null, null, false, false);

      if (layerParentEnt == null)
         return null;

      DirEnt layerDirEnt = lookupPackage(layerParentEnt.subDirs, layerFile, EntType.LayerGroup, layer.packagePrefix, null, false, false);

      if (layerDirEnt == null)
         return null;

      String layerType = src.relTypeName;
      String fileDir = CTypeUtil.getPackageName(layerType);
      String fileTail = CTypeUtil.getClassName(layerType);

      if (fileDir != null) {
         layerDirEnt = lookupPackage(layerDirEnt.subDirs, fileDir, EntType.Type, layer.packagePrefix, layer, true, false);
      }
      if (layerDirEnt == null)
         return null;

      TreeEnt foundEnt = null;
      for (int i = 0; i < layerDirEnt.entries.size(); i++) {
         TreeEnt ent = layerDirEnt.entries.get(i);
         if (ent.value.equals(fileTail)) {
            foundEnt = ent;
            layerDirEnt.entries.remove(i);
            layerDirEnt.removeEntry(foundEnt);
            break;
         }
      }
      return foundEnt;
   }

   TreeEnt removeTypeFromLayerTree(ITypeDeclaration itd) {
      if (!(itd instanceof BodyTypeDeclaration))
         return null;

      BodyTypeDeclaration td = (BodyTypeDeclaration) itd;
      Layer layer = td.layer;
      if (layer == null)
         return null;

      JavaModel model = td.getJavaModel();
      SrcEntry src = model.srcFile;

      String layerName = layer.layerName;
      String layerGroup = CTypeUtil.getPackageName(layerName);
      String layerFile = CTypeUtil.getClassName(layerName);
      DirEnt layerParentEnt = rootLayerDirEnt;
      if (layerGroup != null)
         layerParentEnt = lookupPackage(rootLayerDirEnt.subDirs, layerGroup, EntType.LayerGroup, null, null, false, false);

      if (layerParentEnt == null)
         return null;

      DirEnt layerDirEnt = lookupPackage(layerParentEnt.subDirs, layerFile, EntType.LayerGroup, layer.packagePrefix, null, false, false);

      if (layerDirEnt == null)
         return null;

      String layerType = CTypeUtil.prefixPath(src.getRelDir(), td.getInnerTypeName());
      String fileDir = CTypeUtil.getPackageName(layerType);
      String fileTail = CTypeUtil.getClassName(layerType);

      if (fileDir != null) {
         layerDirEnt = lookupPackage(layerDirEnt.subDirs, fileDir, EntType.Type, layer.packagePrefix, layer, true, false);
      }
      if (layerDirEnt == null)
         return null;

      TreeEnt foundEnt = null;
      for (int i = 0; i < layerDirEnt.entries.size(); i++) {
         TreeEnt ent = layerDirEnt.entries.get(i);
         if (ent.value.equals(fileTail)) {
            foundEnt = ent;
            layerDirEnt.entries.remove(i);
            layerDirEnt.removeEntry(foundEnt);
            break;
         }
      }
      return foundEnt;
   }

   TreeEnt removeLayerFromLayerTree(Layer layer, boolean remove) {
      String layerName = layer.layerName;
      String layerGroup = CTypeUtil.getPackageName(layerName);
      String layerFile = CTypeUtil.getClassName(layerName);
      DirEnt layerParentEnt = rootLayerDirEnt;
      if (layerGroup != null)
         layerParentEnt = lookupPackage(rootLayerDirEnt.subDirs, layerGroup, EntType.LayerGroup, null, null, false, false);

      if (layerParentEnt == null)
         return null;

      DirEnt layerDirEnt = lookupPackage(layerParentEnt.subDirs, layerFile, EntType.LayerGroup, layer.packagePrefix, null, false, false);

      if (layerDirEnt == null)
         return null;

      if (remove) {
         layerParentEnt.subDirs.remove(layerFile);
         layerParentEnt.removeEntry(layerDirEnt);
      }
      else {
         layerDirEnt.type = EntType.InactiveLayer;
         layerDirEnt.value = CTypeUtil.getClassName(layer.layerDirName);
         layerDirEnt.srcTypeName = layer.layerDirName;
         layerDirEnt.subDirs.clear();
         layerDirEnt.entries.clear();
      }

      return layerDirEnt;
   }

   TreeEnt findTypeInLayerTree(BodyTypeDeclaration td) {
      Layer typeLayer = td.getLayer();

      DirEnt layerDirEnt = lookupPackage(rootLayerDirEnt.subDirs, typeLayer.layerName, EntType.LayerGroup, typeLayer.packagePrefix, null, false, false);
      if (layerDirEnt == null)
         return null;

      int i = 0;
      for (TreeEnt ent:layerDirEnt.entries) {
         if (ent.srcTypeName.equals(td.getFullTypeName())) {
            return ent;
         }
         i++;
      }

      return null;
   }

   void addNewModel(ILanguageModel m) {
      if (!typeTreeBuilt || !layerTreeBuilt)
         return;

      TypeDeclaration td = m.getUnresolvedModelTypeDeclaration();
      boolean needsRefresh = false;
      if (td != null) {
         String typeName = td.fullTypeName;
         if (!nodeExists(typeName)) {
            TreeEnt e = addModelToTypeTree(typeName, m.getPrependPackage());
            if (e != null) {
               e.processEntry();
               needsRefresh = true;
            }
         }
         TreeEnt childEnt = findTypeInLayerTree(td);
         if (childEnt != null) {
            if (childEnt.transparent) {
               childEnt.transparent = false;
            }
         }
         else {
            TreeEnt e = addModelToLayerTree(m, m.getPrependPackage());
            if (e != null) {
               e.processEntry();
               needsRefresh = true;
            }
         }
      }
      // Now still need to go and refresh the visible nodes so we add a new one for this guy.
      if (needsRefresh)
         refresh();
   }

   void pruneChildren(BodyTypeDeclaration td) {
      Object[] innerTypes = ModelUtil.getAllInnerTypes(td, null, true);
      if (innerTypes != null) {
         for (Object innerType:innerTypes) {
            if (innerType instanceof BodyTypeDeclaration) {
               BodyTypeDeclaration btd = (BodyTypeDeclaration) innerType;
               String fullTypeName = btd.getFullTypeName();
               if (system.getSrcTypeDeclaration(fullTypeName, null, true) == null)
                  removeTypeFromTypeTree(fullTypeName);
               else
                  pruneChildren(btd);
            }
         }
      }

   }

   abstract boolean nodeExists(String typeName);

   void removeModel(ILanguageModel m) {
      if (!typeTreeBuilt || !layerTreeBuilt)
         return;

      // Has been removed so use the unresolved type here
      TypeDeclaration td = m.getUnresolvedModelTypeDeclaration();
      boolean needsRefresh = false;
      if (td != null) {
         String typeName = td.fullTypeName;
         if (!nodeExists(typeName))
            return;

         TreeEnt e;

         // Only remove from the type tree if this is the last file defining this type
         if (system.getSrcTypeDeclaration(typeName, null, true) == null) {
            e = removeTypeFromTypeTree(typeName);
            if (e != null) {
               needsRefresh = true;
            }

            // In this case, not removing any of the inner types - we detach the tree parent tree node and so discard the children automatically.
         }
         else {
            // But if there's another version of the same type, we do need to see if any sub-objects have been removed.
            pruneChildren(td);
         }

         e = removeModelFromLayerTree(m);
         if (e != null)
            needsRefresh = true;

      }
      // Now still need to go and refresh the visible nodes so we add a new one for this guy.
      if (needsRefresh)
         refresh();
   }

   void addNewType(ITypeDeclaration itd) {
      if (!typeTreeBuilt || !layerTreeBuilt || !(itd instanceof BodyTypeDeclaration))
         return;

      BodyTypeDeclaration td = (BodyTypeDeclaration) itd;

      boolean needsRefresh = false;
      if (td != null) {
         String typeName = td.fullTypeName;
         if (!nodeExists(typeName)) {
            TreeEnt e = addModelToTypeTree(typeName, true);
            if (e != null) {
               e.processEntry();
               needsRefresh = true;
            }
         }
         TreeEnt childEnt = findTypeInLayerTree(td);
         if (childEnt != null) {
            if (childEnt.transparent) {
               childEnt.transparent = false;
            }
         }
         else {
            TreeEnt e = addTypeToLayerTree(td);
            if (e != null) {
               e.processEntry();
               needsRefresh = true;
            }
         }
      }
      // Now still need to go and refresh the visible nodes so we add a new one for this guy.
      if (needsRefresh)
         refresh();
   }

   void removeType(ITypeDeclaration td) {
      if (!typeTreeBuilt || !layerTreeBuilt)
         return;

      boolean needsRefresh = false;
      if (td != null) {
         String typeName = td.fullTypeName;
         if (!nodeExists(typeName))
            return;

         TreeEnt e;

         // Only remove from the type tree if this is the last file defining this type
         if (system.getSrcTypeDeclaration(typeName, null, true) == null) {
            e = removeTypeFromTypeTree(typeName);
            if (e != null) {
               needsRefresh = true;
            }
         }
         e = removeTypeFromLayerTree(td);
         if (e != null)
            needsRefresh = true;
      }
      // Now still need to go and refresh the visible nodes so we add a new one for this guy.
      if (needsRefresh)
         refresh();
   }


   void removeLayer(Layer layer) {
      if (!typeTreeBuilt || !layerTreeBuilt)
         return;

      boolean needsRefresh = false;
      TreeEnt e = removeLayerFromTypeTree(layer, true);
      if (e != null) {
         needsRefresh = true;
      }
      // TODO: and also if we add "removeLayer" set last arg to true
      e = removeLayerFromLayerTree(layer, false);
      if (e != null)
         needsRefresh = true;

      // Now still need to go and refresh the visible nodes so we add a new one for this guy.
      if (needsRefresh)
         refresh();
   }

   DirEnt lookupPackage(Map<String,DirEnt> index, String pkgName, EntType type, String srcPrefix, Layer layer, boolean create, boolean isTypeTree) {
      String root = CTypeUtil.getHeadType(pkgName);
      String tail = CTypeUtil.getTailType(pkgName);

      if (root == null) {
         DirEnt ents = index.get(pkgName);
         if (ents == null) {
            if (!create)
               return null;

            ents = new DirEnt(type, pkgName, isTypeTree, CTypeUtil.prefixPath(srcPrefix, pkgName), layer);
            ents.prependPackage = true;

            String srcTypeName = ents.srcTypeName = CTypeUtil.prefixPath(srcPrefix, pkgName);

            // If this name is defined in an import and we do not have a src file for it, set the imported flag.
            ents.imported = system.getImportDecl(null, null, CTypeUtil.getClassName(srcTypeName)) != null;

            ents.hasSrc = system.getSrcTypeDeclaration(srcTypeName, null, true) != null;

            index.put(pkgName, ents);
         }
         return ents;
      }
      else {
         DirEnt ents = index.get(root);

         // Layer dir's should replace InactiveLayers when we add them.
         if (type == EntType.LayerDir && ents.type == EntType.InactiveLayer)
            ents = null;

         if (ents == null) {
            if (!create)
               return null;

            ents = new DirEnt(type, root, isTypeTree, (type == EntType.LayerGroup ? "layerGroup:" : "") + CTypeUtil.prefixPath(srcPrefix, root), layer);
            ents.imported = false;
            ents.hasSrc = true;
            ents.prependPackage = true;
            index.put(root, ents);
         }

         Map<String,DirEnt> childIndex = ents.subDirs;
         return lookupPackage(childIndex, tail, type, CTypeUtil.prefixPath(srcPrefix, root), layer, create, isTypeTree);
      }
   }

   /** ---- Layer tree ---- */

   boolean rebuildLayerDirEnts() {
      DirEnt rootEnts = new DirEnt(EntType.Root, "By Layer", false, null, null);
      rootLayerDirEnt = rootEnts;

      for (int i = 0; i < system.layers.size(); i++) {
         Layer layer = system.layers.get(i);
         if (layer.getVisibleInEditor() && !isFilteredPackage(layer.packagePrefix))
            addLayerDirEnt(layer);

         if (++layersCreated >= MAX_LAYERS) {
            System.out.println("*** skipping layers due to MAX_LAYERS setting: " + MAX_LAYERS);
            break;
         }

      }

      TreeEnt cent = layerEmptyCommentNode = new TreeEnt(EntType.Comment, "<No visible layers>", false, null, null);

      Map<String,LayerIndexInfo> allLayerIndex = system.getAllLayerIndex();
      for (LayerIndexInfo lii:allLayerIndex.values()) {
          if (!includeInactive)
             break;
         // Do not replace a system layer with one from the index
         //if (system.getLayerByDirName(lii.layerDirName) == null) {
            String layerDirName = lii.layerDirName;
            String layerGroup = CTypeUtil.getPackageName(layerDirName);

            DirEnt pkgEnts;
            if (layerGroup != null)
               pkgEnts = lookupPackage(rootEnts.subDirs, layerGroup, EntType.LayerGroup, null, null, true, false);
            else
               pkgEnts = rootEnts;

            TreeEnt ent = new TreeEnt(EntType.InactiveLayer, CTypeUtil.getClassName(lii.layerDirName), false, lii.layerDirName, null);
            ent.prependPackage = true;

            pkgEnts.entries.add(ent);
         //}
      }
      rootEnts.processEntry();

      return true;
   }

   DirEnt addLayerDirEnt(Layer layer) {
      return addLayerFilesWithName(layer, layer, false);
   }

   int typesCreated = 0;
   int layersCreated = 0;

   void addLayerType(String layerType, Layer srcLayer, Layer fileLayer, boolean transparentLayer, DirEnt layerDirEnt, boolean prependPackage, boolean imported, boolean addInnerTypes) {
       String fileDir = CTypeUtil.getPackageName(layerType);
       String fileTail = CTypeUtil.getClassName(layerType);

       DirEnt layerParent;
       if (fileDir == null) {
          layerParent = layerDirEnt;
       }
       else {
          layerParent = lookupPackage(layerDirEnt.subDirs, fileDir, EntType.Type, fileLayer.packagePrefix, fileLayer, true, false);
       }

       if (transparentLayer) {
          boolean found = false;
          // Do not add elements which are already here
          if (layerParent.hasChild(fileTail))
             return;
       }

       boolean isLayerFile = fileDir == null && fileTail.equals(CTypeUtil.getClassName(srcLayer.layerName));

       if (!transparentLayer || !isLayerFile) {
          TreeEnt ent = new TreeEnt(isLayerFile ? EntType.LayerFile : EntType.Type, fileTail, false, prependPackage && !imported ? CTypeUtil.prefixPath(fileLayer.packagePrefix, layerType) : layerType, fileLayer);

          if (isFilteredType(ent.srcTypeName))
             return;

          // Here we are filtering types just to limit the number for testing - but don't filter if we've already go the type in the type tree.
          // This let's us test more functionality and creates a reasonable subset of the types.
          if (++typesCreated >= MAX_TYPES && getTypeTreeEnt(ent.srcTypeName) == null) {
             System.out.println("*** Skipping type: " + ent.srcTypeName + " due to MAX_TYPES: " + MAX_TYPES);
             return;
          }

          // If this entity is imported into this layer from outside, set the imported flag
          ent.imported = fileLayer.getImportDecl(fileTail, false) != null;
          ent.transparent = transparentLayer;
          ent.prependPackage = prependPackage;
          if (loadInnerTypesAtStartup) {
             Object td = ent.typeDeclaration;
             JavaModel jm = null;
             if (td != null)
                jm = ModelUtil.getJavaModel(td);
             if (ent.type == EntType.LayerFile)
                ent.hasSrc = true;
             else
                ent.hasSrc = fileLayer.findSrcFile(jm == null ? layerType : jm.getSrcFile().relFileName, true) != null;
          }
          else {
             ent.hasSrc = true; // TODO: can't we compute this without parsing the type!
          }

          layerParent.entries.add(ent);

          if (loadInnerTypesAtStartup && addInnerTypes) {
             Object typeDecl = ent.typeDeclaration;
             if (typeDecl != null) {
                Set<String> innerTypes = srcLayer.getInnerTypeNames(layerType, typeDecl, true);
                if (innerTypes != null) {
                   for (String innerType:innerTypes) {
                      addLayerType(innerType, srcLayer, fileLayer, transparentLayer, layerDirEnt, true, false, false);
                   }
                }
             }
          }
       }
   }

   /**
     * The srcLayer is used to retrieve the source file names.  The fileLayer is the layer used to register those files.  They are
     * the same unless transparentLayer is true... in that case, we are adding the files from srcFile to fileLayer's tree unless those files
     * already exist in fileLayer
     */
   DirEnt addLayerFilesWithName(Layer srcLayer, Layer fileLayer, boolean transparentLayer) {
      String layerName = fileLayer.layerName;
      String layerGroup = CTypeUtil.getPackageName(layerName);
      String layerFile = CTypeUtil.getClassName(layerName);
      DirEnt layerParentEnt = rootLayerDirEnt;
      if (layerGroup != null)
         layerParentEnt = lookupPackage(rootLayerDirEnt.subDirs, layerGroup, EntType.LayerGroup, null, null, true, false);

      //TreeEnt ent = new TreeEnt();
      //ent.srcTypeName = layer.layerUniqueName;
      //ent.value = layerFile;
      //ent.type = EntType.Layer;
      //layerParentEnt.entries.add(ent);

      DirEnt layerDirEnt = lookupPackage(layerParentEnt.subDirs, layerFile, EntType.LayerDir, fileLayer.packagePrefix, null, true, false);
      layerDirEnt.type = EntType.LayerDir; // Might exist as Inactive if we're loading this new

      layerDirEnt.layer = fileLayer;
      Set<SrcEntry> layerSrcEntries = srcLayer.getSrcEntries();
      for (SrcEntry srcEnt:layerSrcEntries) {
         String layerType = srcEnt.relTypeName;
         addLayerType(layerType, srcLayer, fileLayer, transparentLayer, layerDirEnt, srcEnt.prependPackage, false, true);
      }
      // Add the layer file itself
      addLayerType(layerFile, srcLayer, fileLayer, false, layerDirEnt, true, false, true);

      //Set<String> layerImported = srcLayer.getImportedTypeNames();
      //for (String importedType:layerImported) {
         // Do not set prependPackage here since the imported types are always absolute and not relative to this layer necessarily
      //   addLayerType(importedType, srcLayer, fileLayer, transparentLayer, layerDirEnt, false, true, false);
      //}

      // If this layer is transparent, any any files which exist in the base layers
      if (fileLayer.transparent && fileLayer.baseLayers != null && !transparentLayer) {
         for (Layer base:fileLayer.baseLayers) {
            addLayerFilesWithName(base, fileLayer, true);
         }
      }

      // Transparent layers will already have inherited the imported types
      if (!transparentLayer) {
         // Adding imported names.  If the layer prefix matches, keep the file organization.  If not, add these types
         // to the top level.  If they are imported, their names must not conflict.  Layers can then reorganize types
         // in different packages.
         for (String layerFullType:fileLayer.getImportedTypeNames()) {
            String layerType;
            if (layerFullType.startsWith(fileLayer.packagePrefix)) {
               int pplen = fileLayer.packagePrefix.length();
               layerType = pplen == 0 ? layerFullType : layerFullType.substring(pplen+1);
            }
            else {
               layerType = CTypeUtil.getClassName(layerFullType);
            }
            String fileDir = CTypeUtil.getPackageName(layerType);
            String fileTail = CTypeUtil.getClassName(layerType);
            DirEnt layerParent;
            if (fileDir == null)
               layerParent = layerDirEnt;
            else
               layerParent = lookupPackage(layerDirEnt.subDirs, fileDir, EntType.Type, fileLayer.packagePrefix, fileLayer, true, false);

            if (!layerParent.hasChild(fileTail)) {
               TreeEnt ent = new TreeEnt(EntType.Type, fileTail, false, layerFullType, fileLayer);
               ent.prependPackage = true;
               // If this entity is imported into this layer from outside, set the imported flag
               ent.imported = true;
               ent.hasSrc = fileLayer.findSrcFile(layerType.replace('.', '/'), true) != null;
               layerParent.entries.add(ent);
            }
         }
      }

      return layerDirEnt;
   }

   void addNewLayer(Layer layer) {
      if (!typeTreeBuilt || !layerTreeBuilt)
         return;

      DirEnt ent = addLayerDirEnt(layer);
      ent.processEntry();

      // Then build our DirEnt structure from the Set of src type names we get back
      Set<String> srcTypeNames = layer.getSrcTypeNames(true, true, false, true);
      for (String srcTypeName:srcTypeNames) {
         // TODO: need to fix the setting of prependPackage - to handle files in the type tree
         TreeEnt e = addModelToTypeTree(srcTypeName, true);
      }
      // Re-process everything?
      rootTypeDirEnt.processEntry();

      refresh();
   }

   TreeEnt {
      // When needsType is true, we set the cachedTypeDeclaration property which syncs it to the client
      needsType =: fetchType();

      void fetchType() {
         if (needsType) {
            cachedTypeDeclaration = getTypeDeclaration();
            processEntry();
         }
      }

      boolean hasErrors() {
         Object td = getTypeDeclaration();
         if (td instanceof BodyTypeDeclaration) {
            JavaModel model = ((BodyTypeDeclaration) td).getJavaModel();
            return editorModel.ctx.hasErrors(model);
         }
         return false;
      }

      boolean needsSave() {
         Object td = getTypeDeclaration();
         if (td instanceof BodyTypeDeclaration) {
            JavaModel model = ((BodyTypeDeclaration) td).getJavaModel();
            return editorModel.ctx.modelChanged(model);
         }
         return false;
      }

      Object getTypeDeclaration() {
         if (typeName == null)
            return null;
         if (cachedTypeDeclaration != null)
            return cachedTypeDeclaration;
         if (type == EntType.LayerFile) {
            Layer layer = system.getLayerByTypeName(typeName);
            if (layer != null) {
               return layer.model.getModelTypeDeclaration();
            }
            return null;
         }

         // First try to get the src version
         Object res = system.getSrcTypeDeclaration(typeName, null, prependPackage);
         // Fall back to the class but only for things which are real types.
         if (res == null && prependPackage)
            res = system.getClassWithPathName(typeName);
         return res;
      }

      boolean getTypeIsVisible() {
         // TODO: method to find the set of layers for a file name - filter those layers
         if (cachedTypeDeclaration != null || loadInnerTypesAtStartup) {
            Object type = getTypeDeclaration();
            if (type != null) {
               if (!ModelUtil.matchesLayerFilter(type, codeTypes, codeFunctions)) {
                  return false;
               }
               if (!createMode && !ModelUtil.isApplicationType(type))
                  return false;
            }
         }
         return true;
      }

      void processEntry() {
         // type is already assigned for these types
         UIIcon newIcon = null;
         switch (type) {
            // NOTE: Package cannot be in this list due the fact that getSrcTypeNames returns inner classes in front of
            // outer classes.  When we do lookupPackage we create the parent node as a package.  We don't then reset it
            // when we add the type with the same name.
            case Root:
               open = true;
               // Fall through to set the icon
            case LayerGroup:
            case InactiveLayer:
            case Primitive:
               newIcon = findIcon();
               if (newIcon != null && newIcon != icon)
                  icon = newIcon;
               return;
            case LayerDir:
            case LayerFile:
               if (layer != null) {
                  // We already pull these out of the layer so no need to set them here.  They are used in TypeIsVisible which is not used for layers anyway.
                  //entCodeTypes = new ArrayList<CodeType>(Collections.singletonList(layer.codeType));
                  //entCodeFunctions = new ArrayList<CodeFunction>(Collections.singletonList(layer.codeFunction));
                  newIcon = findIcon();
                  if (newIcon != null && newIcon != icon)
                     icon = newIcon;
               }
               return;
         }
         if (newIcon != null && newIcon != icon)
            icon = newIcon;

         Object typeDecl = loadInnerTypesAtStartup ? getTypeDeclaration() : cachedTypeDeclaration;
         if (typeDecl != null) {
            EntType newType = null;
            switch (ModelUtil.getDeclarationType(typeDecl)) {
               case CLASS:
                  newType = EntType.ParentType;
                  break;
               case OBJECT:
                  newType = EntType.ParentObject;
                  break;
               case INTERFACE:
                  newType = EntType.ParentInterface;
                  break;
               case ENUM:
                  newType = EntType.ParentEnum;
                  break;
               case ENUMCONSTANT:
                  newType = EntType.ParentEnumConstant;
                  break;
               default:
                  newType = EntType.ParentType;
                  break;
            }
            // This can get called twice, once again after we set the type declaration (if we did not load types at startup)
            if (newType != null && newType != type)
               type = newType;

            // Only set these for nodes with types.
            if (entCodeTypes == null) {
               entCodeTypes = new ArrayList<CodeType>();
               entCodeFunctions = new ArrayList<CodeFunction>();
               if (isTypeTree || layer == null) {
                   ModelUtil.getFiltersForType(typeDecl, entCodeTypes, entCodeFunctions, isTypeTree);
               }
               // For typeDir ents, we don't want the most specific layer of the type only the layer associated with the directory
               else {
                  entCodeTypes.add(layer.codeType);
                  entCodeFunctions.add(layer.codeFunction);
               }
            }
         }
         else {
            Layer layer = system.getLayerByName(srcTypeName);
            if (layer == null) {
               layer = system.getLayerByTypeName(srcTypeName);
            }
            if (layer != null) {
               type = EntType.LayerFile;
            }
            // Package
         }
         icon = findIcon();
      }

      boolean isDynamic() {
         Object type = loadInnerTypesAtStartup ? getTypeDeclaration() : getCachedTypeDeclaration();
         if (type == null)
            return false;
         return ModelUtil.isDynamicType(type);
      }

      UIIcon findIcon() {
        switch (type) {
           case Root:
           case Comment:
              return null;
           case ParentType:
           case Type:
              if (isDynamic())
                 return GlobalResources.classDynIcon;
              else
                 return GlobalResources.classIcon;
           case ParentObject:
           case Object:
              if (isDynamic())
                 return GlobalResources.objectDynIcon;
              else
                 return GlobalResources.objectIcon;
           case ParentEnum:
           case ParentEnumConstant:
           case EnumConstant:
           case Enum:
              if (isDynamic())
                 return GlobalResources.enumDynIcon;
              else
                 return GlobalResources.enumIcon;
           case ParentInterface:
           case Interface:
              if (isDynamic())
                 return GlobalResources.interfaceDynIcon;
              else
                 return GlobalResources.interfaceIcon;
           case LayerDir:
           case LayerFile:
              if (layer != null && layer.dynamic)
                 return GlobalResources.layerDynIcon;
              else
                 return GlobalResources.layerIcon;
           case InactiveLayer:
              return GlobalResources.inactiveLayerIcon;
           case Primitive:
              String tn = value;
              if (tn.equals("String") || tn.equals("char"))
                 return GlobalResources.stringIcon;
              else if (tn.equals("int") || tn.equals("short") || tn.equals("byte") || tn.equals("long"))
                 return GlobalResources.intIcon;
              else if (tn.equals("float") || tn.equals("double"))
                 return GlobalResources.floatIcon;
              else if (tn.equals("boolean"))
                 return GlobalResources.booleanIcon;
              else
                 System.err.println("*** Unknown primitive type: " + tn);
         }
         return null;
      }
   }

   DirEnt {
      subDirs = new LinkedHashMap<String,DirEnt>();
      entries = new ArrayList<TreeEnt>();

      void processEntry() {
         super.processEntry();

         Collections.sort(entries);
         for (DirEnt childEnt:subDirs.values()) {
             childEnt.processEntry();
         }
         // Find all of the sub-dirs which have sub-types for them
         for (TreeEnt childEnt:entries) {
            childEnt.processEntry();
         }

         // Accumulate the entCodeTypes and Functions from all child nodes so that we know whether or not to
         // show a DirEnt even when it's subDirs and entries are not populated on the client.
         if (entCodeTypes == null || entCodeFunctions == null) {
            entCodeTypes = new ArrayList<CodeType>();
            entCodeFunctions = new ArrayList<CodeFunction>();
            for (DirEnt childEnt:subDirs.values()) {
               addCodeTypes(childEnt.entCodeTypes, entCodeTypes);
               addCodeFunctions(childEnt.entCodeFunctions, entCodeFunctions);
            }
            // Find all of the sub-dirs which have sub-types for them
            for (TreeEnt childEnt:entries) {
               addCodeTypes(childEnt.entCodeTypes, entCodeTypes);
               addCodeFunctions(childEnt.entCodeFunctions, entCodeFunctions);
            }
         }
      }

      private void addCodeTypes(ArrayList<CodeType> srcTypes, ArrayList<CodeType> dstTypes) {
         if (srcTypes == null)
            return;
         for (int i = 0; i < srcTypes.size(); i++) {
            CodeType ct = srcTypes.get(i);
            if (!dstTypes.contains(ct))
               dstTypes.add(ct);
         }
      }

      private void addCodeFunctions(ArrayList<CodeFunction> src, ArrayList<CodeFunction> dst) {
         if (src == null)
            return;
         for (int i = 0; i < src.size(); i++) {
            CodeFunction s = src.get(i);
            if (!dst.contains(s))
               dst.add(s);
         }
      }

      void fetchType() {
         if (needsType) {
            super.fetchType();
            if (cachedTypeDeclaration != null) {
               initChildren();
            }
         }
      }
   }

   void stop() {
      if (listener != null) {
         system.removeNewModelListener(listener);
      }
   }
}

Program Editor Synchronization

It's worth looking in detail on how synchronization works with this model. Properties can be sent to the client either because they are registered globally or lazily when they are accessed by the top-level page object during rendering.

All of that information is sent to the client in the initial response so it can be used to populate Javascript objects. It comes after the static HTML generated by the server tag objects. The information serialized to the cilent can then be used by the Javascript code to update any portions of the UI that are not "server only" content in the first refresh.

There's a lot of complex behavior, with only a few lines of explicit synchronization code. It might seem like magic, but it's actually very easy to trace what's going on and make it do what you want by setting annotations and occasionally a using the API. Data binding events are used to track synchronized property changes. A method call to addS yncType is inserted to register the metadat and addSyncInst is used to signal a new instance of a given type was created that needs to be tracked. Lazy references are processed when StrataCode serializes a reference to a new instance that has not been synchronized. It can automatically synchronize it at that time.

StrataCode makes it easy to follow what's going on. It uses the SCLanguage to synchronize the client and server. Instead of JSON, you see a layer of changes going back and forth.

Here is the initial layer of data for the program editor in layer format (abbreviated to avoid repetition). The program editor code defines UIIcons using the global mode so those come across first: First send over global/static state:

package sc.gui.util;
     
UIIcon {
   static sc.gui.util.UIIcon UIIcon__0 = new sc.gui.util.UIIcon("/sc/editor/", "images/class.gif", "Class");
}
UIIcon__0 {
   dir = "/sc/editor/";
   path = "images/class.gif";
   desc = "Class";
}

.... more icons ...

Then comes the data which describes the LayeredSystem (the container for the current stack of layers), the layer info, the tree of available types in the system, and lazily pulls down the meta-data for a selected type. These objects are marked as "on-demand". The objects in the page we are loading refers to them. That first reference causes them to be added to the layer. They come across first because they do not depend on anything else.

package sc.layer;

object LayeredSystem__0 extends sc.layer.LayeredSystem {
}
package sc.editor;

EditorFrame.body.editorPanel.editorModel {
   system = sc.layer.LayeredSystem__0;
   currentTypeIsLayer = false;
   staleCompiledModel = false;
}
package sc.lang;

object JLineInterpreter__0 extends sc.lang.JLineInterpreter {
}

Then properties on the editorModel and typeTreeModel instances are set. These are simple objects which extend the classes shown above:

package sc.editor;

EditorFrame.body.editorPanel.editorModel {
   ctx = sc.lang.JLineInterpreter__0;
   triggeredByUndo = false;
   debugEnabled = true;
}
EditorFrame.body.editorPanel.typeTreeModel {
   system = sc.layer.LayeredSystem__0;
   typeListener = EditorFrame.body.editorPanel.trees.typeList.typeTree;
   layerListener = EditorFrame.body.editorPanel.trees.layerList.layerTree;
   object DirEnt__0 extends sc.editor.TypeTreeModel.DirEnt {
      imported = false;
      hasSrc = false;
      transparent = false;
      prependPackage = false;
      entries = {};
   }
   rootTypeDirEnt = EditorFrame.body.editorPanel.typeTreeModel.DirEnt__0;
   DirEnt__0 {
      type = TypeTreeModel.EntType.Root;
      value = "All Types";
      isTypeTree = true;
   }
   object TreeEnt__0 extends sc.editor.TypeTreeModel.TreeEnt {
      imported = false;
      hasSrc = false;
      transparent = false;
      prependPackage = false;
   }
   typeEmptyCommentNode = EditorFrame.body.editorPanel.typeTreeModel.TreeEnt__0;
   TreeEnt__0 {
      type = TypeTreeModel.EntType.Comment;
      value = "No visible types";
      isTypeTree = true;
   }

   ... Repeated for each tree entry...

When the UIColor node in the type tree is selected, the client sends down this:

package sc.editor;

EditorFrame.body.editorPanel.typeTreeModel.TreeEnt__5 {
   open = true;
   needsType = true;
   selected = true;
}

The server code modifies the TreeEnt class like this:

   TreeEnt {
      // When needsType is true, we set the cachedTypeDeclaration property which syncs it to the client
      needsType =: fetchType();

      void fetchType() {
         if (needsType) {
            cachedTypeDeclaration = getTypeDeclaration();
            processEntry();
         }
      }
   }
So when needsType is set to true, cachedTypeDeclaration gets populated with the data we need on the client. The server tracks this change and puts this into the sync response:
object sc_type_UIColor__0 extends sc.lang.java.ClassDeclaration {
}
package sc.editor;

EditorFrame.body.editorPanel.typeTreeModel.TreeEnt__5 {
   cachedTypeDeclaration = sc_type_UIColor__0;
}
package ;

sc_type_UIColor__0 {
   typeName = "UIColor";
   layer = sc.layer.Layer__0;
   packageName = "sc.gui.util";
   dynamicType = false;
   isLayerType = false;
}
package sc.lang.java;

object VariableDefinition__0 extends sc.lang.java.VariableDefinition {
}
object VariableDefinition__1 extends sc.lang.java.VariableDefinition {
}
object VariableDefinition__2 extends sc.lang.java.VariableDefinition {
}
object VariableDefinition__3 extends sc.lang.java.VariableDefinition {
}
package ;

sc_type_UIColor__0 {
   allProperties = {sc.lang.java.VariableDefinition__0, sc.lang.java.VariableDefinition__1, sc.lang.java.VariableDefinition__2, sc.lang.java.VariableDefinition__3};
   declarationType = sc.lang.java.DeclarationType.CLASS;
}
package sc.lang.java;

VariableDefinition__0 {
   variableName = "class";
}
VariableDefinition__1 {
   variableName = "r";
   layer = sc.layer.Layer__0;
}
VariableDefinition__2 {
   variableName = "g";
   layer = sc.layer.Layer__0;
}
VariableDefinition__3 {
   variableName = "b";
   layer = sc.layer.Layer__0;
}

With the StrataCode serialization protocol, not only does it serialize your data, it also wires together a complete graph of objects. It then monitors changes to those objects and exchanges deltas back and forth.

The StrataCode serialization protocol is translated to Javascript on the server and is shipped to the client as Javascript code. The client eval's this script to apply the changes. You can even set breakpoints to walk through the deserialization.

A binary protocol for the sync system is planned to make large volume data transfers more efficient.

Tree Widget
Here's the tree widget, used to generate the HTML for each of the two tree's in StrataCode. It shows data binding expressions being used to define the content and behavior of the tree. Data binding is used to choose the CSS classes for each tag. It's used to make tags visible/invisible. To conditionally render an icon for the node in the tree.

file: editor/js/core/TreeView.schtml
<li class=':= tree != null && tree.hasChildren ? (tree.ent.open ? "sctTreeNode sctParentOpen" : "sctTreeNode sctParentClosed") : "sctTreeNode sctLeaf"'>
   <%! TypeTreeModel.TreeNode tree; %>

   <a href="#" clickEvent="=: tree.ent.toggleOpen()" class=':= tree != null && tree.ent.selected ? "selectedItem" : ""'>
      <img height="18" width="18" src=':= tree != null && tree.ent.open ? "/images/listClose.png" : "/images/listOpen.png"' visible=':= tree != null && tree.needsOpenClose' style="vertical-align:-4px">
      <img src='= tree == null || tree.ent.icon == null ? "" : tree.ent.icon.path' visible=":= tree.ent.icon != null" style="vertical-align:-3px">
      <%= tree.ent.value %>
   </a>
   <ol visible=":= tree != null && tree.hasChildren" class=':= tree != null && tree.ent.open ? "sctOpen" : "sctClosed"'>
      <%! TypeTreeModel.DirTreeNode dirTree := tree instanceof TypeTreeModel.DirTreeNode ? (TypeTreeModel.DirTreeNode) tree : null; %>
      <li repeat=":= dirTree.children"  repeatVarName="childTree" extends="TreeView" tree=":= childTree"/>
   </ol>
</li>
Look at the tag has the repeat attribute. It extends the TreeView component - i.e. it extends its enclosing class. That gives us the nested behavior you see in the tree, all from one simple component.