/// <summary> /// Fill the function variables with names. /// </summary> private void FillVariableNames() { List <Symbol> variablesToFill = fn.Variables; for (int i = 0; i < variablesToFill.Count; i++) { Symbol sym = (Symbol)variablesToFill[i]; sym.m_values = null; sym.m_value = 0; object sometypeofobject = Apsim.Get(Object as Model, sym.m_name.Trim()); if (sometypeofobject == null) { throw new Exception("Cannot find variable: " + sym.m_name + " while evaluating expression: " + expression); } if (sometypeofobject is double) { sym.m_value = (double)sometypeofobject; } else if (sometypeofobject is int) { sym.m_value = Convert.ToDouble(sometypeofobject, System.Globalization.CultureInfo.InvariantCulture); } else if (sometypeofobject is double[]) { sym.m_values = (double[])sometypeofobject; } else if (sometypeofobject is double[][]) { double[][] allvalues = sometypeofobject as double[][]; List <double> singleArrayOfValues = new List <double>(); foreach (double[] dimension in allvalues) { foreach (double value in dimension) { singleArrayOfValues.Add(value); } } sym.m_values = (double[])singleArrayOfValues.ToArray(); } variablesToFill[i] = sym; } fn.Variables = variablesToFill; }
/// <summary>Documents the specified model.</summary> /// <param name="modelNameToDocument">The model name to document.</param> /// <param name="tags">The auto doc tags.</param> /// <param name="headingLevel">The starting heading level.</param> public void DocumentModel(string modelNameToDocument, List <AutoDocumentation.ITag> tags, int headingLevel) { Simulation simulation = Apsim.Find(this, typeof(Simulation)) as Simulation; if (simulation != null) { // Find the model of the right name. IModel modelToDocument = Apsim.Find(simulation, modelNameToDocument); // Get the path of the model (relative to parentSimulation) to document so that // when replacements happen below we will point to the replacement model not the // one passed into this method. string pathOfSimulation = Apsim.FullPath(simulation) + "."; string pathOfModelToDocument = Apsim.FullPath(modelToDocument).Replace(pathOfSimulation, ""); // Clone the simulation Simulation clonedSimulation = Apsim.Clone(simulation) as Simulation; // Make any substitutions. MakeSubstitutions(new Simulation[] { clonedSimulation }); // Now use the path to get the model we want to document. modelToDocument = Apsim.Get(clonedSimulation, pathOfModelToDocument) as IModel; // resolve all links in cloned simulation. Apsim.ResolveLinks(clonedSimulation); foreach (Model child in Apsim.ChildrenRecursively(clonedSimulation)) { Apsim.ResolveLinks(child); } // Document the model. modelToDocument.Document(tags, headingLevel, 0); // Unresolve links. Apsim.UnresolveLinks(clonedSimulation); foreach (Model child in Apsim.ChildrenRecursively(clonedSimulation)) { Apsim.UnresolveLinks(child); } } }
/// <summary>Documents the specified model.</summary> /// <param name="modelNameToDocument">The model name to document.</param> /// <param name="tags">The auto doc tags.</param> /// <param name="headingLevel">The starting heading level.</param> public void DocumentModel(string modelNameToDocument, List <AutoDocumentation.ITag> tags, int headingLevel) { Simulation simulation = Apsim.Find(this, typeof(Simulation)) as Simulation; if (simulation != null) { // Find the model of the right name. IModel modelToDocument = Apsim.Find(simulation, modelNameToDocument); // If not found then find a model of the specified type. if (modelToDocument == null) { modelToDocument = Apsim.Get(simulation, "[" + modelNameToDocument + "]") as IModel; } // If the simulation has the same name as the model we want to document, dig a bit deeper if (modelToDocument == simulation) { modelToDocument = Apsim.ChildrenRecursivelyVisible(simulation).FirstOrDefault(m => m.Name.Equals(modelNameToDocument, StringComparison.OrdinalIgnoreCase)); } // If still not found throw an error. if (modelToDocument != null) { // Get the path of the model (relative to parentSimulation) to document so that // when replacements happen below we will point to the replacement model not the // one passed into this method. string pathOfSimulation = Apsim.FullPath(simulation) + "."; string pathOfModelToDocument = Apsim.FullPath(modelToDocument).Replace(pathOfSimulation, ""); // Clone the simulation Simulation clonedSimulation = Apsim.Clone(simulation) as Simulation; // Make any substitutions. MakeSubstitutions(clonedSimulation); // Now use the path to get the model we want to document. modelToDocument = Apsim.Get(clonedSimulation, pathOfModelToDocument) as IModel; if (modelToDocument == null) { throw new Exception("Cannot find model to document: " + modelNameToDocument); } // resolve all links in cloned simulation. Links.Resolve(clonedSimulation); modelToDocument.IncludeInDocumentation = true; foreach (IModel child in Apsim.ChildrenRecursively(modelToDocument)) { child.IncludeInDocumentation = true; } // Document the model. AutoDocumentation.DocumentModel(modelToDocument, tags, headingLevel, 0, documentAllChildren: true); // Unresolve links. Links.Unresolve(clonedSimulation); } } }
/// <summary> /// Parse a string into documentation tags /// </summary> /// <param name="stringToParse">The string to parse</param> /// <param name="model">The associated model where the string came from</param> /// <param name="tags">The list of tags to add to</param> /// <param name="headingLevel">The current heading level</param> /// <param name="indent">The current indent level</param> /// <param name="documentAllChildren">Ensure all children are documented?</param> public static void ParseTextForTags(string stringToParse, IModel model, List <ITag> tags, int headingLevel, int indent, bool documentAllChildren) { List <IModel> childrenDocumented = new List <Core.IModel>(); int numSpacesStartOfLine = -1; string paragraphSoFar = string.Empty; if (stringToParse.StartsWith("\r\n")) { stringToParse = stringToParse.Remove(0, 2); } StringReader reader = new StringReader(stringToParse); string line = reader.ReadLine(); int targetHeadingLevel = headingLevel; while (line != null) { line = line.Trim(); // Adjust heading levels. if (line.StartsWith("#")) { int currentHeadingLevel = line.Count(c => c == '#'); targetHeadingLevel = headingLevel + currentHeadingLevel - 1; // assumes models start numbering headings at 1 '#' character string hashString = new string('#', targetHeadingLevel); line = hashString + line.Replace("#", "") + hashString; } if (line != string.Empty) { { if (numSpacesStartOfLine == -1) { int preLineLength = line.Length; line = line.TrimStart(); numSpacesStartOfLine = preLineLength - line.Length - 1; } else { line = line.Remove(0, numSpacesStartOfLine); } } } // Remove expression macros and replace with values. line = RemoveMacros(model, line); string heading; int thisHeadingLevel; if (GetHeadingFromLine(line, out heading, out thisHeadingLevel)) { StoreParagraphSoFarIntoTags(tags, indent, ref paragraphSoFar); tags.Add(new Heading(heading, thisHeadingLevel)); } else if (line.StartsWith("[Document ")) { StoreParagraphSoFarIntoTags(tags, indent, ref paragraphSoFar); // Find child string childName = line.Replace("[Document ", "").Replace("]", ""); IModel child = Apsim.Get(model, childName) as IModel; if (child == null) { paragraphSoFar += "<b>Unknown child name: " + childName + " </b>\r\n"; } else { DocumentModel(child, tags, targetHeadingLevel + 1, indent); childrenDocumented.Add(child); } } else if (line.StartsWith("[DocumentType ")) { StoreParagraphSoFarIntoTags(tags, indent, ref paragraphSoFar); // Find children string childTypeName = line.Replace("[DocumentType ", "").Replace("]", ""); Type childType = ReflectionUtilities.GetTypeFromUnqualifiedName(childTypeName); foreach (IModel child in Apsim.Children(model, childType)) { DocumentModel(child, tags, targetHeadingLevel + 1, indent); childrenDocumented.Add(child); } } else if (line == "[DocumentView]") { tags.Add(new ModelView(model)); } else { paragraphSoFar += " " + line + "\r\n"; } line = reader.ReadLine(); } StoreParagraphSoFarIntoTags(tags, indent, ref paragraphSoFar); if (documentAllChildren) { // write children. foreach (IModel child in Apsim.Children(model, typeof(IModel))) { if (!childrenDocumented.Contains(child)) { DocumentModel(child, tags, headingLevel + 1, indent, documentAllChildren); } } } }
/// <summary>Writes the description of a class to the tags.</summary> /// <param name="model">The model to get documentation for.</param> /// <param name="tags">The tags to add to.</param> /// <param name="headingLevel">The heading level to use.</param> /// <param name="indent">The indentation level.</param> /// <param name="documentAllChildren">Document all children?</param> /// <param name="withHeading">Put a heading in for this model?</param> public static void DocumentModel(IModel model, List <ITag> tags, int headingLevel, int indent, bool documentAllChildren = false, bool withHeading = false) { if (doc == null) { string fileName = Path.ChangeExtension(Assembly.GetExecutingAssembly().Location, ".xml"); doc = new XmlDocument(); doc.Load(fileName); } if (withHeading) { tags.Add(new AutoDocumentation.Heading(model.Name, headingLevel)); } List <IModel> childrenDocumented = new List <Core.IModel>(); string nameToFindInSummary = "members/T:" + model.GetType().FullName.Replace("+", ".") + "/summary"; XmlNode summaryNode = XmlUtilities.Find(doc.DocumentElement, nameToFindInSummary); if (summaryNode != null) { int numSpacesStartOfLine = -1; string paragraphSoFar = string.Empty; string st = summaryNode.InnerXml; if (st.StartsWith("\r\n")) { st = st.Remove(0, 2); } StringReader reader = new StringReader(st); string line = reader.ReadLine(); int targetHeadingLevel = headingLevel; while (line != null) { line = line.Trim(); // Adjust heading levels. if (line.StartsWith("#")) { int currentHeadingLevel = line.Count(c => c == '#') / 2; targetHeadingLevel = headingLevel + currentHeadingLevel - 1; string hashString = new string('#', targetHeadingLevel); line = hashString + line.Replace("#", "") + hashString; } if (line != string.Empty) { { if (numSpacesStartOfLine == -1) { int preLineLength = line.Length; line = line.TrimStart(); numSpacesStartOfLine = preLineLength - line.Length - 1; } else { line = line.Remove(0, numSpacesStartOfLine); } } } string heading; int thisHeadingLevel; if (GetHeadingFromLine(line, out heading, out thisHeadingLevel)) { StoreParagraphSoFarIntoTags(tags, indent, ref paragraphSoFar); tags.Add(new Heading(heading, thisHeadingLevel)); } else if (line.StartsWith("[Document ")) { StoreParagraphSoFarIntoTags(tags, indent, ref paragraphSoFar); // Find child string childName = line.Replace("[Document ", "").Replace("]", ""); IModel child = Apsim.Get(model, childName) as IModel; if (child == null) { paragraphSoFar += "<b>Unknown child name: " + childName + " </b>\r\n"; } else { child.Document(tags, targetHeadingLevel + 1, indent); childrenDocumented.Add(child); } } else { paragraphSoFar += " " + line + "\r\n"; } line = reader.ReadLine(); } StoreParagraphSoFarIntoTags(tags, indent, ref paragraphSoFar); } if (documentAllChildren) { // write children. foreach (IModel child in Apsim.Children(model, typeof(IModel))) { if (!childrenDocumented.Contains(child)) { child.Document(tags, headingLevel + 1, indent); } } } }
/// <summary> /// Internal [link] resolution algorithm. /// </summary> /// <param name="obj"></param> /// <param name="allModels">A collection of all model wrappers</param> private void ResolveInternal(object obj, List <ModelWrapper> allModels) { // Go looking for [Link]s foreach (IVariable field in GetAllDeclarations(GetModel(obj), GetModel(obj).GetType(), BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public, allLinks: true)) { LinkAttribute link = field.GetAttribute(typeof(LinkAttribute)) as LinkAttribute; if (link != null) { // Get the field type or the array element if it is an array field. Type fieldType = field.DataType; if (fieldType.IsArray) { fieldType = fieldType.GetElementType(); } else if (field.DataType.Name.StartsWith("List") && field.DataType.GenericTypeArguments.Length == 1) { fieldType = field.DataType.GenericTypeArguments[0]; } // Try and get a match from our services first. List <object> matches; matches = services.FindAll(s => fieldType.IsAssignableFrom(s.GetType())); // If no match on services then try other options. if (matches.Count == 0 && obj is IModel) { Simulation parentSimulation = Apsim.Parent(obj as IModel, typeof(Simulation)) as Simulation; if (fieldType.IsAssignableFrom(typeof(ILocator)) && parentSimulation != null) { matches.Add(new Locator(obj as IModel)); } else if (fieldType.IsAssignableFrom(typeof(IEvent)) && parentSimulation != null) { matches.Add(new Events(obj as IModel)); } } // If no match on services then try other options. if (matches.Count == 0) { // Get a list of models that could possibly match. if (link is ParentLinkAttribute) { matches = new List <object>(); matches.Add(GetParent(obj, fieldType)); } else if (link is LinkByPathAttribute) { object match = Apsim.Get(obj as IModel, (link as LinkByPathAttribute).Path); if (match != null) { matches.Add(match); } } else if (link.IsScoped(field)) { matches = GetModelsInScope(obj, allModels); } else { matches = GetChildren(obj); } } // Filter possible matches to those of the correct type. matches.RemoveAll(match => !fieldType.IsAssignableFrom(GetModel(match).GetType())); // If we should use name to match then filter matches to those with a matching name. if (link.UseNameToMatch(field)) { matches.RemoveAll(match => !StringUtilities.StringsAreEqual(GetName(match), field.Name)); } if (field.DataType.IsArray) { Array array = Array.CreateInstance(fieldType, matches.Count); for (int i = 0; i < matches.Count; i++) { array.SetValue(GetModel(matches[i]), i); } field.Value = array; } else if (field.DataType.Name.StartsWith("List") && field.DataType.GenericTypeArguments.Length == 1) { var listType = typeof(List <>); var constructedListType = listType.MakeGenericType(fieldType); IList array = Activator.CreateInstance(constructedListType) as IList; for (int i = 0; i < matches.Count; i++) { array.Add(GetModel(matches[i])); } field.Value = array; } else if (matches.Count == 0) { if (!link.IsOptional) { throw new Exception("Cannot find a match for link " + field.Name + " in model " + GetFullName(obj)); } } else if (matches.Count >= 2 && !link.IsScoped(field)) { throw new Exception(string.Format(": Found {0} matches for link {1} in model {2} !", matches.Count, field.Name, GetFullName(obj))); } else { field.Value = GetModel(matches[0]); } } } }
/// <summary> /// Internal [link] resolution algorithm. /// </summary> /// <param name="obj"></param> private void ResolveInternal(object obj) { foreach (IVariable field in GetAllDeclarations(GetModel(obj), GetModel(obj).GetType(), BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public, allLinks: true)) { LinkAttribute link = field.GetAttribute(typeof(LinkAttribute)) as LinkAttribute; if (link != null) { Type fieldType = field.DataType; if (fieldType.IsArray) { fieldType = fieldType.GetElementType(); } else if (field.DataType.Name.StartsWith("List") && field.DataType.GenericTypeArguments.Length == 1) { fieldType = field.DataType.GenericTypeArguments[0]; } List <object> matches; matches = services.FindAll(s => fieldType.IsAssignableFrom(s.GetType())); if (matches.Count == 0 && obj is IModel) { Simulation parentSimulation = Apsim.Parent(obj as IModel, typeof(Simulation)) as Simulation; if (fieldType.IsAssignableFrom(typeof(ILocator)) && parentSimulation != null) { matches.Add(new Locator(obj as IModel)); } else if (fieldType.IsAssignableFrom(typeof(IEvent)) && parentSimulation != null) { matches.Add(new Events(obj as IModel)); } } if (matches.Count == 0) { if (link is ParentLinkAttribute) { matches = new List <object>(); matches.Add(GetParent(obj, fieldType)); } else if (link is LinkByPathAttribute) { object match = Apsim.Get(obj as IModel, (link as LinkByPathAttribute).Path); if (match != null) { matches.Add(match); } } else if (link.IsScoped(field)) { matches = Apsim.FindAll(obj as IModel).Cast <object>().ToList(); } else { matches = GetChildren(obj); } } matches.RemoveAll(match => !fieldType.IsAssignableFrom(GetModel(match).GetType())); if (link.UseNameToMatch(field)) { matches.RemoveAll(match => !StringUtilities.StringsAreEqual(GetName(match), field.Name)); } if (field.DataType.IsArray) { Array array = Array.CreateInstance(fieldType, matches.Count); for (int i = 0; i < matches.Count; i++) { array.SetValue(GetModel(matches[i]), i); } field.Value = array; } else if (field.DataType.Name.StartsWith("List") && field.DataType.GenericTypeArguments.Length == 1) { var listType = typeof(List <>); var constructedListType = listType.MakeGenericType(fieldType); IList array = Activator.CreateInstance(constructedListType) as IList; for (int i = 0; i < matches.Count; i++) { array.Add(GetModel(matches[i])); } field.Value = array; } else if (matches.Count == 0) { if (!link.IsOptional) { throw new Exception("Cannot find a match for link " + field.Name + " in model " + GetFullName(obj)); } } else if (matches.Count >= 2 && !link.IsScoped(field)) { throw new Exception(string.Format(": Found {0} matches for link {1} in model {2} !", matches.Count, field.Name, GetFullName(obj))); } else { field.Value = GetModel(matches[0]); } } } }