From 6584510d390a37153c20974da6704a907058fea0 Mon Sep 17 00:00:00 2001 From: mike Date: Mon, 19 Oct 2009 04:38:32 +0000 Subject: Merge gitorious@git.sugarlabs.org:tutorius/michaeljm-dev into merge_michaeljm-dev --- (limited to 'tutorius/bundler.py') diff --git a/tutorius/bundler.py b/tutorius/bundler.py index 8808d93..56bbf3e 100644 --- a/tutorius/bundler.py +++ b/tutorius/bundler.py @@ -48,6 +48,8 @@ INI_XML_FSM_PROPERTY = "FSM_FILENAME" INI_FILENAME = "meta.ini" TUTORIAL_FILENAME = "tutorial.xml" NODE_COMPONENT = "Component" +NODE_SUBCOMPONENT = "property" +NODE_SUBCOMPONENTLIST = "listproperty" class TutorialStore(object): @@ -150,6 +152,71 @@ class XMLSerializer(Serializer): eventfiltersList = stateNode.appendChild(self._create_event_filters_node(state.get_event_filter_list(), doc)) return statesList + def _create_addon_component_node(self, parent_attr_name, comp, doc): + """ + Takes a component that is embedded in another component (e.g. the content + of a OnceWrapper) and encapsulate it in a node with the property name. + + e.g. + + + + + + + When reloading this node, we should look up the property name for the parent + in the attribute of the node, then examine the subnode to create the addon + object itself. + + @param parent_attr_name The name of the parent's attribute for this addon + e.g. the OnceWrapper has the action attribute, which corresponds to a + sub-action it must execute once. + @param comp The component node itself + @param doc The XML document root (only used to create the nodes) + @returns A NODE_SUBCOMPONENT node, with the property attribute and a sub node + that represents another component. + """ + subCompNode = doc.createElement(NODE_SUBCOMPONENT) + subCompNode.setAttribute("name", parent_attr_name) + + subNode = self._create_component_node(comp, doc) + + subCompNode.appendChild(subNode) + + return subCompNode + + def _create_addonlist_component_node(self, parent_attr_name, comp_list, doc): + """ + Takes a list of components that are embedded in another component (ex. the + content of a ChainAction) and encapsulate them in a node with the property + name. + + e.g. + + + + + + + + When reloading this node, we should look up the property name for the parent + in the the attribute of the node, then rebuild the list by appending the + content of all the subnodes. + + @param parent_attr_name The name of the parent component's property + @param comp_list A list of components that comprise the property + @param doc The XML document root (only for creating new nodes) + @returns A NODE_SUBCOMPONENTLIST node with the property attribute + """ + subCompListNode = doc.createElement(NODE_SUBCOMPONENTLIST) + subCompListNode.setAttribute("name", parent_attr_name) + + for comp in comp_list: + compNode = self._create_component_node(comp, doc) + subCompListNode.appendChild(compNode) + + return subCompListNode + def _create_component_node(self, comp, doc): """ Takes a single component (action or eventfilter) and transforms it @@ -169,10 +236,10 @@ class XMLSerializer(Serializer): for propname in comp.get_properties(): propval = getattr(comp, propname) if getattr(type(comp), propname).type == "addonlist": - for subval in propval: - compNode.appendChild(self._create_component_node(subval, doc)) - elif getattr(type(comp), propname).type == "addonlist": - compNode.appendChild(self._create_component_node(subval, doc)) + compNode.appendChild(self._create_addonlist_component_node(propname, propval, doc)) + elif getattr(type(comp), propname).type == "addon": + #import rpdb2; rpdb2.start_embedded_debugger('pass') + compNode.appendChild(self._create_addon_component_node(propname, propval, doc)) else: # repr instead of str, as we want to be able to eval() it into a # valid object. @@ -282,6 +349,27 @@ class XMLSerializer(Serializer): # Error : none of these directories contain the tutorial raise IOError(2, "Neither the global nor the bundle directory contained the tutorial with GUID %s"%guid) + def _get_direct_descendants_by_tag_name(self, node, name): + """ + Searches in the list of direct descendants of a node to find all the node + that have the given name. + + This is used because the Document.getElementsByTagName() function returns the + list of all the descendants (whatever their distance to the start node) that + have that name. In the case of complex components, we absolutely need to inspect + a single layer of the tree at the time. + + @param node The node from which we want the direct descendants with a particular + name + @param name The name of the node + @returns A list, possibly empty, of direct descendants of node that have this name + """ + return_list = [] + for childNode in node.childNodes: + if childNode.nodeName == name: + return_list.append(childNode) + return return_list + def _load_xml_properties(self, properties_elem): """ Changes a list of properties into fully instanciated properties. @@ -298,7 +386,7 @@ class XMLSerializer(Serializer): @param filters_elem An XML Element representing a list of event filters """ reformed_event_filters_list = [] - event_filter_element_list = filters_elem.getElementsByTagName(NODE_COMPONENT) + event_filter_element_list = self._get_direct_descendants_by_tag_name(filters_elem, NODE_COMPONENT) new_event_filter = None for event_filter in event_filter_element_list: @@ -309,6 +397,42 @@ class XMLSerializer(Serializer): return reformed_event_filters_list + def _load_xml_subcomponents(self, node, properties): + """ + Loads all the subcomponent node below the given node and inserts them with + the right property name inside the properties dictionnary. + + @param node The parent node that contains one or many property nodes. + @param properties A dictionnary where the subcomponent property names + and the instantiated components will be stored + @returns Nothing. The properties dict will contain the property->comp mapping. + """ + subCompList = self._get_direct_descendants_by_tag_name(node, NODE_SUBCOMPONENT) + + for subComp in subCompList: + property_name = subComp.getAttribute("name") + internal_comp_node = self._get_direct_descendants_by_tag_name(subComp, NODE_COMPONENT)[0] + internal_comp = self._load_xml_component(internal_comp_node) + properties[str(property_name)] = internal_comp + + def _load_xml_subcomponent_lists(self, node, properties): + """ + Loads all the subcomponent lists below the given node and stores them + under the correct property name for that node. + + @param node The node from which we want to read the subComponent lists + @param properties The dictionnary that will contain the mapping of prop->subCompList + @returns Nothing. The values are returns inside the properties dict. + """ + listOf_subCompListNode = self._get_direct_descendants_by_tag_name(node, NODE_SUBCOMPONENTLIST) + for subCompListNode in listOf_subCompListNode: + property_name = subCompListNode.getAttribute("name") + subCompList = [] + for subCompNode in self._get_direct_descendants_by_tag_name(subCompListNode, NODE_COMPONENT): + subComp = self._load_xml_component(subCompNode) + subCompList.append(subComp) + properties[str(property_name)] = subCompList + def _load_xml_component(self, node): """ Loads a single addon component instance from an Xml node. @@ -318,20 +442,23 @@ class XMLSerializer(Serializer): @return The addon component object of the correct type according to the XML description """ - new_action = addon.create(node.getAttribute("Class")) - if not new_action: - return None + class_name = node.getAttribute("Class") + + properties = {} - for attrib in node.attributes.keys(): - if attrib == "Class": continue - # security note: keep sandboxed - setattr(new_action, attrib, eval(node.getAttribute(attrib), {}, {})) + for prop in node.attributes.keys(): + if prop == "Class" : continue + # security : keep sandboxed + properties[str(prop)] = eval(node.getAttribute(prop)) - # recreate complex attributes - for sub in node.childNodes: - name = getattr(new_action, sub.nodeName) - if name == "addon": - setattr(new_action, sub.getAttribute("Name"), self._load_xml_action(sub)) + # Read the complex attributes + self._load_xml_subcomponents(node, properties) + self._load_xml_subcomponent_lists(node, properties) + + new_action = addon.create(class_name, **properties) + + if not new_action: + return None return new_action @@ -342,7 +469,7 @@ class XMLSerializer(Serializer): @param actions_elem An XML Element representing a list of Actions """ reformed_actions_list = [] - actions_element_list = actions_elem.getElementsByTagName(NODE_COMPONENT) + actions_element_list = self._get_direct_descendants_by_tag_name(actions_elem, NODE_COMPONENT) for action in actions_element_list: new_action = self._load_xml_component(action) -- cgit v0.9.1