/// <summary> /// Decode an XML list of variables and add the variables to the worksapce. /// </summary> /// <param name="xmlVariables"> xmlVariables List of XML vale elements.riab</param> /// <param name="workspace"> The worksapce to which the variable should be added</param> public static void DomToVariables(XmlNode xmlVariables, Workspace workspace) { for (int i = 0; i < xmlVariables.ChildNodes.Count; i++) { XmlNode xmlChild = xmlVariables.ChildNodes[i]; var type = xmlChild.GetAttribute("type"); var id = xmlChild.GetAttribute("id"); var name = xmlChild.InnerXml; if (type == null) { throw new Exception("Variable with id, " + id + " is without a type"); } workspace.CreateVariable(name, type, id); } }
/// <summary> /// Updates all blocks related to a specific procedure with respect to name, arguments, and /// whether the definition can contain a statement sequence. If any of the optional arguments are /// null, the existing values from the blocks are used. /// </summary> /// <param name="originalProcedureName">The name of the procedure, before this method.</param> /// <param name="updatedProcedure">The info with which to update procedure mutators.</param> /// <param name="argIndexUpdates">A list of mappings from original argument index to new index.</param> public void MutateProcedure(string originalProcedureName, Procedure updatedProcedure, List <ProcedureArgumentIndexUpdate> argIndexUpdates = null) { Block definition = GetDefinitionBlock(originalProcedureName); if (definition == null) { throw new Exception("Unknown procedure \"" + originalProcedureName + "\""); } List <Block> procedureCalls = GetCallers(originalProcedureName); ProcedureDefinitionMutator definitionMutator = definition.Mutator as ProcedureDefinitionMutator; Procedure oldInfo = definitionMutator.ProcedureInfo; mNameMgr.RemoveDistinctName(originalProcedureName); string newProcedureName = mNameMgr.GetDistinctName(updatedProcedure.Name); bool isFuncRename = !newProcedureName.Equals(originalProcedureName); if (isFuncRename) { mProcedureDefinitions.Remove(originalProcedureName); mProcedureDefinitions[newProcedureName] = definition; mProcedureCallers.Remove(originalProcedureName); mProcedureCallers[newProcedureName] = procedureCalls; } List <string> newArgs = updatedProcedure.Arguments; for (int i = 0; i < newArgs.Count; i++) { string argName = newArgs[i]; if (!Names.IsSafe(argName)) { throw new Exception("Invalid variable name \"" + argName + "\" " + "(argument #" + i + " )"); } // create new variable if (!mWorkspace.HasVariable(argName)) { mWorkspace.CreateVariable(argName); } } definitionMutator.Mutate(updatedProcedure); //mutate each procedure call foreach (Block procRef in procedureCalls) { ProcedureCallMutator callMutator = procRef.Mutator as ProcedureCallMutator; int oldArgCount = callMutator.GetArgumentNameList().Count; Block[] oldValues = new Block[oldArgCount]; //disconnect prior value blocks for (int i = 0; i < oldArgCount; i++) { Input argInput = callMutator.GetArgumenInput(i); Block valueBlock = argInput.ConnectedBlock; if (valueBlock != null) { oldValues[i] = valueBlock; argInput.Connection.Disconnect(); } } callMutator.Mutate(updatedProcedure); //reconnect any blocks to orginal inputs if (argIndexUpdates != null) { foreach (ProcedureArgumentIndexUpdate indexUpdate in argIndexUpdates) { Block originalValue = oldValues[indexUpdate.Before]; if (originalValue != null) { Input argInput = callMutator.GetArgumenInput(indexUpdate.After); argInput.Connection.Connect(originalValue.OutputConnection); } } } } FireUpdate(new ProcedureUpdateData(oldInfo, updatedProcedure)); }
/// <summary> /// Decode an XML block tag and crete a block (and possbly sub blocks) on the /// workspace. /// </summary> /// <param name="xmlBlock"> XML block element.</param> /// <param name="workspace"> The workspace</param> public static Block DomToBlockHeadless(XmlNode xmlBlock, Workspace workspace) { var prototypeName = xmlBlock.GetAttribute("type"); var id = xmlBlock.GetAttribute("id"); Block block = workspace.NewBlock(prototypeName, id); Block blockChild = null; for (var i = 0; i < xmlBlock.ChildNodes.Count; i++) { var xmlChild = xmlBlock.ChildNodes[i]; if (xmlChild.NodeType == (XmlNodeType)3) { // Ignore any text at the <block>level. It's all whitespace anyway. continue; } Input input = null; // Find any enclosed blocks or shadows in this tag. XmlNode childBlockNode = null; XmlNode childShadowNode = null; for (var j = 0; j < xmlChild.ChildNodes.Count; j++) { var grandchildNode = xmlChild.ChildNodes[j]; if (grandchildNode.NodeType == (XmlNodeType)1) { if (string.Equals(grandchildNode.NodeName().ToLower(), "block")) { childBlockNode = grandchildNode; } else if (string.Equals(grandchildNode.NodeName().ToLower(), "shadow")) { childShadowNode = grandchildNode; } } } // Use the shadow block if there is no child block. if (null == childBlockNode && null != childShadowNode) { childBlockNode = childShadowNode; } var name = xmlChild.GetAttribute("name"); switch (xmlChild.NodeName().ToLower()) { case "mutation": // Custom data for an advanced block. if (block.Mutator != null) { block.Mutator.FromXml((XmlElement)xmlChild); } break; case "data": block.Data = xmlChild.TextContent(); break; case "title": case "field": { var field = block.GetField(name); var text = xmlChild.TextContent(); if (field is FieldVariable) { // TODO (marisaleung): When we change setValue and getValue to // interact with id's instead of names,update this so that we get // the variable based on id instead of textContent. var type = string.IsNullOrEmpty(xmlChild.GetAttribute("variableType")) ? xmlChild.GetAttribute("variableType") : ""; var variable = workspace.GetVariable(text); if (null == variable) { variable = workspace.CreateVariable(text, type, xmlChild.GetAttribute(id)); } if (!string.IsNullOrEmpty(type)) { if (!string.Equals(variable.Type, type)) { throw new Exception("Serialized variable type with id \'" + variable.ID + "\' had type " + variable.Type + ", and " + "does not match variable field that references it: " + Xml.DomToText(xmlChild) + "."); } } } if (null == field) { Console.WriteLine("Ignoring non-existent field " + name + " in block " + prototypeName); break; } field.SetValue(text); } break; case "value": case "statement": input = block.GetInput(name); if (null == input) { Console.WriteLine("Ignoring non-existent input " + name + " in block " + prototypeName); break; } if (null != childShadowNode) { // input.connection.setShadowDom(childShadowNode); } if (null != childBlockNode) { blockChild = Xml.DomToBlockHeadless(childBlockNode, workspace); if (null != blockChild.OutputConnection) { input.Connection.Connect(blockChild.OutputConnection); } else if (null != blockChild.PreviousConnection) { input.Connection.Connect(blockChild.PreviousConnection); } else { throw new Exception("Child block does not have output or previous statement."); } } break; case "next": if (null != childShadowNode && null != block.NextConnection) { // block.nextConnection.setShadowDom(childShadowNode); } if (null != childBlockNode) { blockChild = Xml.DomToBlockHeadless(childBlockNode, workspace); block.NextConnection.Connect(blockChild.PreviousConnection); } break; default: // Unknown tag; ignore. Same principle as HTML parsers. Console.WriteLine("Ignoring unknown tag: " + xmlChild.NodeName()); break; } } var inline = xmlBlock.GetAttribute("inline"); if (!string.IsNullOrEmpty(inline)) { block.SetInputsInline(string.Equals(inline, "true")); } var disabled = xmlBlock.GetAttribute("disabled"); if (!string.IsNullOrEmpty(disabled)) { block.Disabled = string.Equals(disabled, "true"); } var deletable = xmlBlock.GetAttribute("deletable"); if (!string.IsNullOrEmpty(deletable)) { block.Deletable = string.Equals(deletable, "true"); } var movable = xmlBlock.GetAttribute("movable"); if (!string.IsNullOrEmpty(movable)) { block.Movable = string.Equals(movable, "true"); } var editable = xmlBlock.GetAttribute("editable"); if (!string.IsNullOrEmpty(editable)) { block.Editable = string.Equals(editable, "true"); } var collapsed = xmlBlock.GetAttribute("collapsed"); if (!string.IsNullOrEmpty(collapsed)) { block.Collapsed = string.Equals(collapsed, "true"); } return(block); }