/// <summary> /// Resolves the shortcut on the specified node. i.e. makes concrete /// </summary> /// <param name="node">The node to remove the shortcut from.</param> /// <exception cref="System.Exception">Cannot find shortcut: + shortcut</exception> private static void ResolveShortcut(XmlNode node) { string shortcut = XmlUtilities.Attribute(node, "shortcut"); XmlNode concreteNode = XmlUtilities.Find(node.OwnerDocument.DocumentElement, shortcut); if (concreteNode == null) { throw new Exception("Cannot find shortcut: " + shortcut); } foreach (XmlNode child in concreteNode.ChildNodes) { // Get the 'name' of the concrete child string childName = XmlUtilities.NameAttr(child); if (childName == string.Empty) { childName = child.Name; } // See if we have a node under shortcutted node with same name as concrete node. XmlNode nodeToReplace = XmlUtilities.Find(node, childName); if (nodeToReplace == null) { node.AppendChild(child.Clone()); } else { // Only replace non shortcutted child nodes. if (XmlUtilities.Attribute(nodeToReplace, "shortcut") == string.Empty) { nodeToReplace.ParentNode.ReplaceChild(child.Clone(), nodeToReplace); } } } }
private static void DocumentNode(StreamWriter OutputFile, XmlNode N, int NextLevel, Model parentModel) { if (N.Name == "Name") { return; } if (XmlUtilities.Attribute(N, "shortcut") != "") { OutputFile.WriteLine("<p>" + XmlUtilities.Value(N, "Name") + " uses the same value as " + XmlUtilities.Attribute(N, "shortcut")); } else if (N.Name == "Constant") { OutputFile.WriteLine(Header(XmlUtilities.Value(N, "Name"), NextLevel, XmlUtilities.Value(N.ParentNode, "Name"))); WriteDescriptionForTypeName(OutputFile, N, parentModel); TryDocumentMemo(OutputFile, N, NextLevel); OutputFile.WriteLine("<p>Value = " + XmlUtilities.Value(N, "Value") + "</p>"); } else if (XmlUtilities.ChildNodes(N, "").Count == 0) { WriteDescriptionForTypeName(OutputFile, N, parentModel); DocumentProperty(OutputFile, N, NextLevel); } else if (XmlUtilities.ChildNodes(N, "XYPairs").Count > 0) { CreateGraph(OutputFile, XmlUtilities.ChildNodes(N, "XYPairs")[0], NextLevel, parentModel); } else if (XmlUtilities.Type(N) == "TemperatureFunction") { DocumentTemperatureFunction(OutputFile, N, NextLevel, parentModel); } //else if (XmlUtilities.Type(N) == "GenericPhase") // DocumentFixedPhase(OutputFile, N, NextLevel); // else if (XmlUtilities.Type(N) == "PhaseLookupValue") // DocumentPhaseLookupValue(OutputFile, N, NextLevel); else if (XmlUtilities.Type(N) == "ChillingPhase") { ChillingPhaseFunction(OutputFile, N, NextLevel); } else if (N.Name == "Memo") { DocumentMemo(OutputFile, N, NextLevel); } else { string childName = XmlUtilities.Value(N, "Name"); Model childModel = null; if (parentModel != null) { childModel = Apsim.Child(parentModel, childName) as Model; } DocumentNodeAndChildren(OutputFile, N, NextLevel, childModel); } }
/// <summary> /// Removes all shortcuts from the specified node and all child nodes. /// </summary> /// <param name="node">The node to remove shortcuts from.</param> /// <returns>The XML node with all shortcuts resolved.</returns> public static void Remove(XmlNode node) { string shortcut = XmlUtilities.Attribute(node, "shortcut"); if (shortcut != string.Empty) { ResolveShortcut(node); } foreach (XmlNode child in node.ChildNodes) { Remove(child); // recursion } }
/// <summary>Finds a direct child of the specified node that has a name starting with a prefix.</summary> /// <param name="node">The node.</param> /// <param name="namePrefix">The beginning of a name attribute to find.</param> /// <returns></returns> private static XmlNode FindChildWithPrefix(XmlNode node, string namePrefix) { if (node != null) { foreach (XmlNode child in node.ChildNodes) { var name = XmlUtilities.Attribute(child, "name"); if (name != null && name.StartsWith(namePrefix)) { return(child); } } } return(null); }
/// <summary> /// Resolves the shortcut on the specified node. i.e. makes concrete /// </summary> /// <param name="node">The node to remove the shortcut from.</param> /// <exception cref="System.Exception">Cannot find shortcut: + shortcut</exception> private static void ResolveShortcut(XmlNode node) { string shortcut = XmlUtilities.Attribute(node, "shortcut"); XmlNode concreteNode = XmlUtilities.Find(node.OwnerDocument.DocumentElement, shortcut); if (concreteNode == null) { throw new Exception("Cannot find shortcut: " + shortcut); } if (!string.IsNullOrWhiteSpace(XmlUtilities.Attribute(concreteNode, "shortcut"))) { // If this happens, the node we are linked to is a shortcut // itself and is further down in the simulations tree than the // current node. In this scenario, we want to resolve the link // and remove the shortcut attribute so we don't resolve the // shortcut a second time in the Remove() method. ResolveShortcut(concreteNode); XmlUtilities.DeleteAttribute(concreteNode, "shortcut"); } foreach (XmlNode child in concreteNode.ChildNodes) { // Get the 'name' of the concrete child string childName = XmlUtilities.NameAttr(child); if (childName == string.Empty) { childName = child.Name; } // See if we have a node under shortcutted node with same name as concrete node. XmlNode nodeToReplace = XmlUtilities.Find(node, childName); if (nodeToReplace == null) { node.AppendChild(child.Clone()); } else { // Only replace non shortcutted child nodes. if (XmlUtilities.Attribute(nodeToReplace, "shortcut") == string.Empty) { nodeToReplace.ParentNode.ReplaceChild(child.Clone(), nodeToReplace); } } } }
/// <summary>Constructor for a genotype from ruminant.prm.</summary> /// <param name="parameterNode">The ruminant.prm xml node where this genotype is defined.</param> public GenotypeWrapper(XmlNode parameterNode) { parameterXmlSections.Add(parameterNode.OuterXml); var parent = parameterNode.ParentNode; while (!(parent is XmlDocument)) { parameterXmlSections.Add(parent.OuterXml); parent = parent.ParentNode; } Name = XmlUtilities.Attribute(parameterNode, "name").Replace(".", ""); AnimalType = XmlUtilities.Value(parameterNode, "animal"); var parentNode = parameterNode.ParentNode; while (AnimalType == string.Empty && parentNode != null) { AnimalType = XmlUtilities.Value(parentNode, "animal"); parentNode = parentNode.ParentNode; } }
/// <summary>Converts XML to the latest version.</summary> /// <param name="rootNode">The root node.</param> /// <param name="fileName">The name of the .apsimx file</param> /// <returns>Returns true if something was changed.</returns> public static bool ConvertToLatestVersion(XmlNode rootNode, string fileName) { string fileVersionString = XmlUtilities.Attribute(rootNode, "Version"); int fileVersion = 0; if (fileVersionString != string.Empty) { fileVersion = Convert.ToInt32(fileVersionString); } // Update the xml if not at the latest version. bool changed = false; while (fileVersion < LastestVersion) { changed = true; // Find the method to call to upgrade the file by one version. int toVersion = fileVersion + 1; MethodInfo method = typeof(APSIMFileConverter).GetMethod("UpgradeToVersion" + toVersion, BindingFlags.NonPublic | BindingFlags.Static); if (method == null) { throw new Exception("Cannot find converter to go to version " + toVersion); } // Found converter method so call it. method.Invoke(null, new object[] { rootNode, fileName }); fileVersion++; } if (changed) { XmlUtilities.SetAttribute(rootNode, "Version", fileVersion.ToString()); } return(changed); }
/// <summary>Sets the soil.</summary> /// <param name="soil">The soil.</param> public void SetSoil(Soil soil) { XmlDocument soilDoc = new XmlDocument(); soilDoc.LoadXml(SoilUtilities.ToXML(soil)); // Name soil crop models foreach (XmlNode soilcrop in XmlUtilities.FindAllRecursivelyByType(soilDoc.DocumentElement, "SoilCrop")) { XmlUtilities.SetValue(soilcrop, "Name", XmlUtilities.Attribute(soilcrop, "name") + "Soil"); } // Name soil samples foreach (XmlNode sample in XmlUtilities.FindAllRecursivelyByType(soilDoc.DocumentElement, "Sample")) { XmlUtilities.SetValue(sample, "Name", XmlUtilities.Attribute(sample, "name")); } XmlNode paddockNode = XmlUtilities.Find(simulationXML, "Zone"); XmlUtilities.EnsureNodeExists(soilDoc.DocumentElement, "SoilNitrogen"); soilDoc.DocumentElement.RemoveChild(XmlUtilities.Find(soilDoc.DocumentElement, "SoilTemperature")); paddockNode.AppendChild(paddockNode.OwnerDocument.ImportNode(soilDoc.DocumentElement, true)); }
/// <summary> /// Pastes the contents of the clipboard. /// </summary> /// <param name="xml">The XML document text</param> /// <param name="parentPath">Path to the parent</param> public void Add(string xml, string parentPath) { try { XmlDocument document = new XmlDocument(); try { document.LoadXml(xml); } catch (XmlException) { MainPresenter.ShowMessage("Invalid XML. Are you sure you're trying to paste an APSIM model?", Simulation.ErrorLevel.Error); } object newModel = XmlUtilities.Deserialise(document.DocumentElement, this.ApsimXFile.GetType().Assembly); // See if the presenter is happy with this model being added. Model parentModel = Apsim.Get(this.ApsimXFile, parentPath) as Model; AllowDropArgs allowDropArgs = new AllowDropArgs(); allowDropArgs.NodePath = parentPath; allowDropArgs.DragObject = new DragObject() { NodePath = null, ModelType = newModel.GetType(), Xml = this.GetClipboardText() }; this.OnAllowDrop(null, allowDropArgs); // If it is happy then issue an AddModelCommand. if (allowDropArgs.Allow) { // If the model xml is a soil object then try and convert from old // APSIM format to new. if (document.DocumentElement.Name == "Soil" && XmlUtilities.Attribute(document.DocumentElement, "Name") != string.Empty) { XmlDocument newDoc = new XmlDocument(); newDoc.AppendChild(newDoc.CreateElement("D")); APSIMImporter importer = new APSIMImporter(); importer.ImportSoil(document.DocumentElement, newDoc.DocumentElement, newDoc.DocumentElement); XmlNode soilNode = XmlUtilities.FindByType(newDoc.DocumentElement, "Soil"); if (soilNode != null && XmlUtilities.FindByType(soilNode, "Sample") == null && XmlUtilities.FindByType(soilNode, "InitialWater") == null) { // Add in an initial water and initial conditions models. XmlNode initialWater = soilNode.AppendChild(soilNode.OwnerDocument.CreateElement("InitialWater")); XmlUtilities.SetValue(initialWater, "Name", "Initial water"); XmlUtilities.SetValue(initialWater, "PercentMethod", "FilledFromTop"); XmlUtilities.SetValue(initialWater, "FractionFull", "1"); XmlUtilities.SetValue(initialWater, "DepthWetSoil", "NaN"); XmlNode initialConditions = soilNode.AppendChild(soilNode.OwnerDocument.CreateElement("Sample")); XmlUtilities.SetValue(initialConditions, "Name", "Initial conditions"); XmlUtilities.SetValue(initialConditions, "Thickness/double", "1800"); XmlUtilities.SetValue(initialConditions, "NO3/double", "10"); XmlUtilities.SetValue(initialConditions, "NH4/double", "1"); XmlUtilities.SetValue(initialConditions, "NO3Units", "kgha"); XmlUtilities.SetValue(initialConditions, "NH4Units", "kgha"); XmlUtilities.SetValue(initialConditions, "SWUnits", "Volumetric"); } document.LoadXml(newDoc.DocumentElement.InnerXml); } IModel child = XmlUtilities.Deserialise(document.DocumentElement, this.ApsimXFile.GetType().Assembly) as IModel; AddModelCommand command = new AddModelCommand(parentModel, document.DocumentElement, this.GetNodeDescription(child), this.view); this.CommandHistory.Add(command, true); } } catch (Exception exception) { this.MainPresenter.ShowMessage(exception.Message, Simulation.ErrorLevel.Error); } }
/// <summary> /// Convert an XML parameter array into a series of commands. /// </summary> /// <param name="parentNode">The XML parameter node.</param> /// <param name="parameterName">The name of the XML child parameter.</param> /// <param name="animalParamName">The name of a GrazPlan parameter.</param> /// <param name="commands">The list of comamnds to add to.</param> /// <param name="numValuesInArray">The number of values that should be in the array.</param> private static void ConvertArrayToCommands(XmlNode parentNode, string parameterName, string animalParamName, List <PropertyReplacement> commands, int numValuesInArray) { var parameterNode = FindChildWithPrefix(parentNode, parameterName); if (parameterNode != null) { var stringValue = parameterNode.InnerText; bool hasMissingValues = stringValue.StartsWith(",") || stringValue.EndsWith(",") || stringValue.Contains(",,"); if (hasMissingValues) { var values = stringValue.Split(','); for (int i = 0; i != values.Length; i++) { if (values[i] != string.Empty) { if (animalParamName == "IntakeLactC") { if (i == 0) { commands.Add(new PropertyReplacement($"FDairyIntakePeak", values[i])); } else { commands.Add(new PropertyReplacement($"{animalParamName}[{i + 1}]", values[i])); } } else { commands.Add(new PropertyReplacement($"{animalParamName}[{i + 2}]", values[i])); // 1 based array indexing before equals sign. } } } } else { // See if an index was specified as part of the parameter name. var nodeName = XmlUtilities.Attribute(parameterNode, "name"); if (nodeName != parameterName) { // There must be an index specified e.g. c-w-0 var index = Convert.ToInt32(nodeName.Replace(parameterName, "")); commands.Add(new PropertyReplacement($"{animalParamName}[{index + 1}]", stringValue)); // 1 based array indexing before equals sign. } else { // Determine if we need to add another value to the top of the values list // so that the number of values matches the array length definition in the animprm.cs code. var values = stringValue.Split(','); if (values.Length != numValuesInArray) { // Add a zero to the top of the list of values. var valuesList = new List <string>(values); valuesList.Insert(0, "0"); values = valuesList.ToArray(); } // We build the string value. stringValue = StringUtilities.BuildString(values, ","); // Create the command. commands.Add(new PropertyReplacement(animalParamName, stringValue)); } } } }
/// <summary>Converts a .apsimx string to the latest version.</summary> /// <param name="st">XML or JSON string to convert.</param> /// <param name="toVersion">The optional version to convert to.</param> /// <param name="fileName">The optional filename where the string came from.</param> /// <returns>Returns true if something was changed.</returns> public static ConverterReturnType DoConvert(string st, int toVersion = -1, string fileName = null) { ConverterReturnType returnData = new ConverterReturnType(); if (toVersion == -1) { toVersion = LatestVersion; } int offset = st.TakeWhile(c => char.IsWhiteSpace(c)).Count(); char firstNonBlankChar = st[offset]; if (firstNonBlankChar == '<') { bool changed = XmlConverters.DoConvert(ref st, Math.Min(toVersion, XmlConverters.LastVersion), fileName); XmlDocument doc = new XmlDocument(); doc.LoadXml(st); int fileVersion = Convert.ToInt32(XmlUtilities.Attribute(doc.DocumentElement, "Version"), CultureInfo.InvariantCulture); if (fileVersion == toVersion) { return new ConverterReturnType() { DidConvert = changed, RootXml = doc } } ; st = ConvertToJSON(st, fileName); returnData.Root = JObject.Parse(st); } else if (firstNonBlankChar == '{') { // json returnData.Root = JObject.Parse(st); } else { throw new Exception("Unknown string encountered. Not JSON or XML. String: " + st); } if (returnData.Root.ContainsKey("Version")) { int fileVersion = (int)returnData.Root["Version"]; if (fileVersion > LatestVersion) { throw new Exception(string.Format("Unable to open file '{0}'. File version is greater than the latest file version. Has this file been opened in a more recent version of Apsim?", fileName)); } // Run converters if not at the latest version. while (fileVersion < toVersion) { returnData.DidConvert = true; // Find the method to call to upgrade the file by one version. int versionFunction = fileVersion + 1; MethodInfo method = typeof(Converter).GetMethod("UpgradeToVersion" + versionFunction, BindingFlags.NonPublic | BindingFlags.Static); if (method == null) { throw new Exception("Cannot find converter to go to version " + versionFunction); } // Found converter method so call it. method.Invoke(null, new object[] { returnData.Root, fileName }); fileVersion++; } if (returnData.DidConvert) { returnData.Root["Version"] = fileVersion; st = returnData.Root.ToString(); } } returnData.DidConvert = EnsureSoilHasInitWaterAndSample(returnData.Root) || returnData.DidConvert; return(returnData); }