Program Editor Example
The program editor contains basic features for build management UIs directly from domain models. For each type in the system, you can view and edit instances of that type. Because the types are partitioned into layers, the forms are also organized via layers. You can use annotations to customize the UI. You can also switch into editing of the types, not just the instances. So you can customize rules, add fields, and add new types. You can edit existing layers, or create new ones to store these customizations.

The swing version

The schtml version
The swing user interface code uses data binding for all layout which is more verbose than html. The JS version is missing some features.

This example shows significant code-reuse across platform the code is easy to navigate and maintain. Building the client/server version from the existing desktop version involved refactoring the existing code as a first step into platform dependent and independent layers. Code which depends on swing went into the swing/core layer, and code that depends on server features, went into the editor.modelImpl layer. Independent code went into the editor.model layer that runs the same way in all three processes: desktop, client and server.

For the client/server version, the model essentially defines the overlapping parts - i.e. the info that's used on both the client and server. In most cases, this layer defines the domain, and view models in a way that maximizes the reusability of this code because it will have no framework dependencies. Most frameworks require some replication of the domain model and view model code for each UI framework or for specifying the service layer or data transfer layers. Instead, in this approach, you use annotations to override the defaults and only resort to explicit service and transfer classes for applications that really need that extra level of control. For code that needs to be customized for a framework, it goes into a framework specific layer.

Here is the model layer of the EditorModel class, which manages the overall view model for the program editor - e.g. the currently selected types and instances, the current layer and other layers that make up the selected types, access to the metadata of the types and propeties. This code is included in both client and server versions of the generated class.

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;
import sc.lang.java.BodyTypeDeclaration;
import sc.lang.InstanceWrapper;
import sc.lang.java.ModelUtil;

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

   List<InstanceWrapper> selectedInstances = null;

   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 */
   @Bindable(crossScope=true)
   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;

   /** Incremented each time a new instance is selected with the same type */
   int newInstSelected = 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 current 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 = new ArrayList<Object>();     // The list of types used to create the form view - removes types filtered by the merge and inherited flags

   @Sync(onDemand=true)
   static 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.

   @Sync(syncMode=SyncMode.Disabled)
   int refreshInstancesCt = 0;
   @Sync(syncMode=SyncMode.Disabled)
   boolean refreshInstancesValid = true;

   void init() {
      SyncManager.initStandardTypes();
      DynUtil.addDynListener(this);
   }

   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, Object inst) {
      if (type == currentType && inst == currentInstance)
         return;

      String newTypeName = null;
      if (type != null) {
         newTypeName = ModelUtil.getTypeName(type);
         String[] newTypeNames = new String[1];
         newTypeNames[0] = newTypeName;
         typeNames = newTypeNames;
      }

      currentType = type;
      currentInstance = inst;
      if (type != null) {
         List<InstanceWrapper> selInstances = new ArrayList<InstanceWrapper>(1);
         selInstances.add(new InstanceWrapper(ctx, inst, newTypeName));
         selectedInstances = selInstances;
      }
      else
         selectedInstances = null;
      selectionChanged++;
   }

   void clearCurrentType() {
      typeNames = new String[0];
      currentType = null;
      currentInstance = 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);

   public boolean filteredProperty(Object type, Object p, boolean perLayer, boolean instanceMode) {
      if (instanceMode) {
         if (p instanceof IVariableInitializer) {
            IVariableInitializer varInit = (IVariableInitializer) p;
            String opStr = varInit.getOperatorStr();
            if (opStr != null && opStr.equals("=:"))
               return true;
         }
      }
      return false;
   }

   public static boolean isConstantProperty(Object prop) {
      if (prop == null)
         return true;
      if (prop instanceof CustomProperty)
         return ((CustomProperty) prop).isConstant();
      return ModelUtil.hasAnnotation(prop, "sc.obj.Constant") || ModelUtil.hasModifier(prop, "final");
   }

   public static boolean isSettableFromString(Object propC, Object propType) {
      return !isConstantProperty(propC) && !(propC instanceof CustomProperty) && sc.type.RTypeUtil.canConvertTypeFromString(propType);
   }

   /** 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))));
   }

   BodyTypeDeclaration processVisibleType(Object typeObj) {
      if (typeObj instanceof BodyTypeDeclaration) {
         return (BodyTypeDeclaration) typeObj;
      }
      return null;
   }

   static String getDisplayNameAnnotation(Object typeOrProp) {
      String name = (String) ModelUtil.getAnnotationValue(typeOrProp, "sc.obj.EditorSettings", "displayName");
      if (name != null && name.length() > 0)
         return name;
      return null;
   }

   static String getPropertyName(Object prop) {
      if (prop == null)
         return ("*** null property");
      if (prop instanceof CustomProperty)
         return ((CustomProperty) prop).name;
      String name = getDisplayNameAnnotation(prop);
      if (name != null)
         return name;
      return ModelUtil.getPropertyName(prop);
   }

   static String getClassDisplayName(Object type) {
      String name = getDisplayNameAnnotation(type);
      if (name != null)
         return name;
      return ModelUtil.getClassName(type);
   }

   boolean isVisible(Object prop) {
      if (prop instanceof CustomProperty)
         return true;
      Boolean vis = (Boolean) ModelUtil.getAnnotationValue(prop, "sc.obj.EditorSettings", "visible");
      if (vis != null && !vis)
         return false;
      return true;
   }

   boolean isReferenceType(Object type) {
      if (ModelUtil.isObjectType(type))
         return true;
      if (ModelUtil.hasAnnotation(type, "sc.obj.ValueObject"))
         return false;
      return true;
   }

   void changeFocus(Object newProp, Object newInst) {
      this.currentProperty = newProp;
      this.currentInstance = newInst;
   }

   void instanceAdded(Object inst) {
      refreshInstances();
   }
   void instanceRemoved(Object inst) {
      if (inst == currentInstance)
         changeCurrentType(currentType, null);
      refreshInstances();
   }
   void refreshInstances() {
      if (refreshInstancesValid) {
         refreshInstancesValid = false;
         DynUtil.invokeLater(new Runnable() {
            void run() {
               refreshInstancesCt++;
               // Doing this after even though we may miss a refresh because we don't want any instances created in the course of this callback to schedule another refresh call.
               refreshInstancesValid = true;
            }
         }, 300);
      }
   }

   void refreshInstancesCheck(Object obj) {
      if (refreshInstancesValid) // This makes sure we do not refresh the bindings unless the instances were valid when this editor was created
         Bind.refreshBinding(obj, "instancesOfType");
   }

   static Layer getLayerForMember(Object prop) {
      if (prop == null || prop instanceof CustomProperty)
         return null;
      return ModelUtil.getLayerForMember(null, prop);
   }

   static Object getPropertyType(Object prop) {
      if (prop instanceof CustomProperty)
         return ((CustomProperty) prop).propertyType;
      return ModelUtil.getPropertyType(prop);
   }

   Object fetchInstanceType(Object inst) {
      Object instType = DynUtil.getType(inst);
      instType = ModelUtil.resolveSrcTypeDeclaration(system, instType);
      return instType;
   }
}
Here the model layer for TypeTreeModel which is the 'view model' class for the tree widget that browsers the types. Types are organized either by type-name, or by layer. Different management UIs or aspects of management UIs might use one or the other or both in combination to provide different easily manageable views of the same types for different audiences. For example, an admin might browse the type tree to find the 'User' object and look at all of the properties combined in all layers. Or use the layered organization of the user properties to find some desired aspect. A back-office employee might instead navigate the layer tree to find the specific view of only the types and properties they need for their task at hand.
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.lang.InstanceWrapper;

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;

   /** Should we also include instances in the type tree.  When included, they are children under their type */
   boolean includeInstances = true;

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

   transient boolean uiBuilt = false;

   @Constant
   ArrayList<String> includePackages;
   @Constant
   ArrayList<String> excludePackages;

   //TypeTree typeTree = new TypeTree(this);
   //ByLayerTypeTree byLayerTypeTree = new ByLayerTypeTree(this);

   object typeTree extends TypeTree {
      treeModel = TypeTreeModel.this;
   }
   object byLayerTypeTree extends ByLayerTypeTree {
      treeModel = TypeTreeModel.this;
   }

   List<TypeTree> typeTrees = new ArrayList<TypeTree>();
   {
      typeTrees.add(typeTree);
      typeTrees.add(byLayerTypeTree);
   }

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

   includeInstances =: 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;

   createMode =: selectionChanged();
   addLayerMode =: selectionChanged();
   createLayerMode =: selectionChanged();
   propertyMode =: refresh();

   // Need to refresh when any new instances are created.  TODO performance: we could reduce the scope of the refresh if necessary here since this might happen a lot in some applications
   int refreshInstancesCt := editorModel.refreshInstancesCt;
   refreshInstancesCt =: refreshInstances();

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

   void refreshInstances() {
      if (rebuildFirstTime || !valid)
         return;
      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 {
         for (TypeTree typeTree:typeTrees) {
            typeTree.refreshTree();
         }
      }
      catch (RuntimeException exc) {
         System.err.println("*** error refreshing tree model: " + exc.toString());
         exc.printStackTrace();
      }
      finally {
         refreshInProgress = false;
      }
   }

   // 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/desktop layer for EditorModel. It's not included in the client version of the EditorModel class, but it is synchronized with the client version. It uses data binding expressions to listen for changes made when synchronization events are received on the client or server. In some situations, those changes will trigger subsequent changes to synchronized properties that will be queued up until the next 'sync' with the other side. This establishes a nice declarative dialog, using a 'do later' on the client and 'end of request' flush on the server.
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;

EditorModel {
   /** Among the typeNames, set to the "currentCtxType" - i.e. the type which has focus. */
   @Bindable(crossScope=true)
   BodyTypeDeclaration currentCtxType := ctx.currentType;

   override @Bindable(crossScope=true)
   currentCtxType =: changeCurrentType(currentCtxType, ctx.currentObject);

   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) {
         return; // in rebuildModel, we change currentLayer which may call this when we are in the midst of updating the model so just ignore it
       }

      if (modelsValid) {
         modelsValid = false;

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

   boolean modelValidating = false;

   // TODO: rename to refreshModel?
   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);
      }

      Object filteredType;

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

      if (filteredType instanceof BodyTypeDeclaration) {
         Object ctxCurrentType = ctx.currentType;
         Layer layer = ctxCurrentType == null ? null : ModelUtil.getLayerForType(system, ctxCurrentType);
         if (ctxCurrentType == null || layer == null || !newFilteredLayers.contains(layer)) {
            if (currentInstance != null)
               ctx.setDefaultCurrentObj((BodyTypeDeclaration) filteredType, currentInstance);
            else if (ctxCurrentType == null || !ModelUtil.isAssignableFrom(filteredType, ctxCurrentType))
               ctx.currentType = (BodyTypeDeclaration) filteredType;
            else {
               // there was a more specific type in the context so we'll use that here
            }
         }
      }
      // else - we are not updating ctx.currentType here - so these two are not in sync when it's a compiled class or the matched type is not in a visible layer.  TODO: not sure this is right.

      currentType = filteredType;

// 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;
      }

      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;

      updateCurrentJavaModel();

      // 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");
      filteredProps.add("serialVersionUID");
   }

   public boolean filteredProperty(Object type, Object p, boolean perLayer, boolean instanceMode) {
      if (super.filteredProperty(type, p, perLayer, instanceMode))
         return true;

      if (!instanceMode) {
         // 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);
   }

   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, "public", system);
          else
            props = ModelUtil.getPropertiesAndTypes(type, "public");
      }
      else {
         if (!inherit && (currentLayer == null || !currentLayer.transparent))
            props = ModelUtil.getDeclaredMergedPropertiesAndTypes(type, "public", true);
         else
            props = ModelUtil.getMergedPropertiesAndTypes(type, "public", system);
      }
      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 updateType, 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, updateType, updateInstances, valueIsExpr);
   }

   String updateCurrentProperty(Object operator, String value, boolean instanceMode) {
      return setElementValue(currentPropertyType, null, currentProperty, operator + value, !instanceMode, 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, Object inst) {
      super.changeCurrentType(type, inst);

      // Push this back if the change is coming from the editor model side
      if (currentCtxType != type && type instanceof BodyTypeDeclaration)
         currentCtxType = (BodyTypeDeclaration) 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, null);
      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, null);

      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/desktop 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 {

   // 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
   static final int MAX_TYPES = 20000; // 100;
   static final int MAX_LAYERS = 20000; // 10;

   boolean includeInactive = false;
   boolean includePrimitives = false;

   boolean isFilteredType(String typeName) {
      if (includePackages == null && excludePackages == null)
         return false;
      if (typeName == null)
         return true;
      if (excludePackages != null) {
         for (String pkg:excludePackages) {
            if (typeName.startsWith(pkg))
               return true;
         }
      }
      if (includePackages != null) {
         for (String pkg:includePackages) {
            if (typeName.startsWith(pkg))
               return false;
         }
      }
      else
         return false;
      return true;
   }

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

   Set<String> getSrcTypeNames() {
      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);
         }
      }
      return srcTypeNames;
   }

   void addNewModel(ILanguageModel m) {
      if (!uiBuilt)
         return;

      TypeDeclaration td = m.getUnresolvedModelTypeDeclaration();
      boolean needsRefresh = false;
      if (td != null) {
         String typeName = td.fullTypeName;
         if (!nodeExists(typeName)) {
            for (TypeTree typeTree:typeTrees) {
               // TODO: hide this difference inside of the TypeTree implementation?
               if (!(typeTree instanceof ByLayerTypeTree)) {
                  TypeTree.TreeEnt e = typeTree.addModel(typeName, m.getPrependPackage());
                  if (e != null) {
                     e.processEntry();
                     needsRefresh = true;
                  }
               }
               else {
                  TypeTree.TreeEnt childEnt = ((ByLayerTypeTree) typeTree).findType(td);
                  if (childEnt != null) {
                     if (childEnt.transparent) {
                        childEnt.transparent = false;
                     }
                  }
                  else {
                     TypeTree.TreeEnt e = ((ByLayerTypeTree) typeTree).addModel(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) {
                  for (TypeTree typeTree:typeTrees)
                     typeTree.removeType(fullTypeName);
               }
               else
                  pruneChildren(btd);
            }
         }
      }
   }

   abstract boolean nodeExists(String typeName);

   void removeModel(ILanguageModel m) {
      if (!uiBuilt)
         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;

         TypeTree.TreeEnt e;

         // Only remove from the type tree if this is the last file defining this type
         if (system.getSrcTypeDeclaration(typeName, null, true) == null) {
            for (TypeTree typeTree:typeTrees) {
               e = typeTree.removeType(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);
         }

         for (TypeTree typeTree:typeTrees) {
            // TODO: move down this method into TypeTree
            if (typeTree instanceof ByLayerTypeTree) {
               e = ((ByLayerTypeTree)typeTree).removeModel(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 (!uiBuilt || !(itd instanceof BodyTypeDeclaration))
         return;

      BodyTypeDeclaration td = (BodyTypeDeclaration) itd;

      boolean needsRefresh = false;
      if (td != null) {
         String typeName = td.fullTypeName;
         for (TypeTree typeTree:typeTrees) {
            if (!(typeTree instanceof ByLayerTypeTree)) {
               if (!nodeExists(typeName)) {
                  TypeTree.TreeEnt e = typeTree.addModel(typeName, true);
                  if (e != null) {
                     e.processEntry();
                     needsRefresh = true;
                  }
               }
            }
            else {
               ByLayerTypeTree blTree = (ByLayerTypeTree) typeTree;
               TypeTree.TreeEnt childEnt = blTree.findType(td);

               if (childEnt != null) {
                  if (childEnt.transparent) {
                     childEnt.transparent = false;
                  }
               }
               else {
                  TypeTree.TreeEnt e = blTree.addType(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 (!uiBuilt)
         return;

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

         TypeTree.TreeEnt e;

         for (TypeTree typeTree:typeTrees) {
            if (!(typeTree instanceof ByLayerTypeTree)) {
               // Only remove from the type tree if this is the last file defining this type
               if (system.getSrcTypeDeclaration(typeName, null, true) == null) {
                  e = typeTree.removeType(typeName);
                  if (e != null) {
                     needsRefresh = true;
                  }
               }
            }
            else {
               e = ((ByLayerTypeTree) typeTree).removeType(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 (!uiBuilt)
         return;

      boolean needsRefresh = false;

      for (TypeTree typeTree:typeTrees) {
         TypeTree.TreeEnt e = typeTree.removeLayer(layer, true);
         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();
   }

   int typesCreated = 0;
   int layersCreated = 0;

   void addNewLayer(Layer layer) {
      if (!uiBuilt)
         return;

      if (byLayerTypeTree != null) {
         TypeTree.TreeEnt ent = byLayerTypeTree.addLayerDirEnt(layer);
         ent.processEntry();
      }

      if (typeTree != null) {
         // 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
            TypeTree.TreeEnt e = typeTree.addModel(srcTypeName, true);
         }
         // Re-process everything?
         typeTree.rootDirEnt.processEntry();
      }

      refresh();
   }


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

Here's the code for the TreeView component for the client/server version. It generates the HTML for each of the two trees in StrataCode on both the client and the server. For an schtml file, the id of the top-level tag is the name of the file so we do not have to specify id='TreeView' here. Data binding is used to specify the css class of the outer li tag and the span which defines the name of the tree element. It's used to call 'toggleOpen' when you click on the +/- iconand whether to show the children list for a given node.

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

   <span class=':= tree != null && tree.ent.selected ? "selectedItem" : ""'>
      <a href="#" clickEvent="=: tree.ent.toggleOpen()">
         <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">
      </a>
      <a href="#" clickEvent="=: tree.ent.selectType(false)">
         <img src='= tree == null || tree.ent.icon == null ? "" : tree.ent.icon.path' visible=":= tree.ent.icon != null" style="vertical-align:-3px">
         <%= tree.ent.nodeId %>
      </a>
   </span>
   <ol visible=":= tree != null && tree.hasChildren" class=':= tree != null && tree.ent.open ? "sctOpen" : "sctClosed"'>
      <li repeat=":= tree.children"  repeatVarName="childTree" extends="TreeView" tree=":= childTree"/>
   </ol>
</li>

The last 'li' tag in the file is the most interesting one. It has the 'repeat' attribute which means it's rendered once for each element in the array. At also extends the TreeView component which also is its enclosing class. That allows it to have an optional set of children of its own and provides unlimited levels of nesting in a simple declarative description.