 				return indent;
 			}
 			
-			//----------------------
-			
 			// for everything else, generate editors for each child in the session state
-			var subtrees:Array = tree.children;
-			
-			if (!(target is ILinkableHashMap))
-			{
-				// sort by class, then by name
-				StandardLib.sort(subtrees, function(st1:WeaveTreeItem, st2:WeaveTreeItem):int {
-					var c1:Class = Object(st1.source).constructor;
-					var c2:Class = Object(st2.source).constructor;
-					var l1:String = st1.label;
-					var l2:String = st2.label;
-					return ObjectUtil.stringCompare(String(c1), String(c2))
-						|| ObjectUtil.stringCompare(l1, l2);
-				});
-			}
-			
-			// editors for enumerated selectable attributes should appear first
-			var owsa:ISelectableAttributes = target as ISelectableAttributes;
-			if (owsa)
-			{
-				var lookup:Dictionary = VectorUtils.createLookup(subtrees, 'source');
-				var names:Array = owsa.getSelectableAttributeNames();
-				subtrees = owsa.getSelectableAttributes()
-					.map(function(attr:*, i:*, a:*):WeaveTreeItem{
-						return subtrees[lookup[attr]]
-							|| sm.getSessionStateTree(attr, names[i]);
-					})
-					.concat(subtrees);
-			}
-			
-			// create child editors
-			var components:Array = [];
-			var ignore:Dictionary = new Dictionary(true);
-			for each (var subtree:WeaveTreeItem in subtrees)
-			{
-				var childLabel:String = subtree.label;
-				var childObject:ILinkableObject = subtree.source;
-				
-				// make sure we don't add duplicate editors
-				if (ignore[childObject])
-					continue;
-				ignore[childObject] = true;
-				
-				var component:IUIComponent = createComponent(childObject, childLabel, true);
-				if (component)
-					components.push(component);
-			}
-			
-			// if there are no child components, there is no session state and no editor.
-			if (components.length == 0)
-				return null;
-			
-			// create a vbox to contain the child editors
-			var vbox:VBox = new VBox();
-			vbox.label = lang(label);
-			vbox.setStyle('verticalGap', 4);
-			UIUtils.pad(vbox, 4, 100, 100);
-			for each (component in components)
-				vbox.addChild(component as DisplayObject);
+			var vbox:VBox = new ComponentUpdater(target, label).vbox;
 			
 			if (!indentChildren)
 				return vbox;

	import weave.ui.Indent;
	import weave.ui.IndentTextInput;
	import weave.ui.SessionStateEditor;
	import weave.utils.VectorUtils;
	
	public class DynamicEditor
	{
		/**
		 * Creates an editor component for an ILinkableObject.
		 * @param obj Either an ILinkableObject or a session state tree generated by SessionManager.getSessionStateTree().
		 * @param label A label for the ILinkableObject.
		 * @param indentChildren If true and the object does not have a simple editor, an Indent component will be created with
		 *                       the specified label in front of a vertical list of child editors. If false, the vertical list
		 *                       will not be indented.
		 */
		public static function createComponent(object:Object, label:String, indentChildren:Boolean):IUIComponent
		{
			var sm:SessionManager = WeaveAPI.SessionManager as SessionManager;
			
			var tree:WeaveTreeItem = object as WeaveTreeItem;
			var target:ILinkableObject = object as ILinkableObject;
			if (target)
				tree = sm.getSessionStateTree(target, label);
			else
				target = tree.source as ILinkableObject;
			
			var indent:Indent;
			
			var lb:LinkableBoolean = target as LinkableBoolean;

	import weave.api.data.IAttributeColumn;
	import weave.api.linkBindableProperty;
	import weave.api.ui.ISelectableAttributes;
	import weave.core.LinkableBoolean;
	import weave.core.LinkableNumber;
	import weave.core.SessionManager;
