private static List <MdFileInfo> FileInfosFromSearchModel(NodeSearchModel nodeSearchModel) { var fileInfos = new List <MdFileInfo>(); foreach (var entry in nodeSearchModel.SearchEntries) { if (MdFileInfo.TryGetMdFileInfoFromSearchEntry(entry, out MdFileInfo info)) { fileInfos.Add(info); } } return(fileInfos); }
private static DynamoDictionaryEntry GetMatchingDictionaryEntry( IEnumerable <DynamoDictionaryEntry> dictEntrys, MdFileInfo info, LayoutSpecification spec) { // The DynamoDictionary only gives information about the name of the node // and the folder path, the MdFileInfo knows about the name of the node and // its namespace. The DynamoDictionary Folder path is the same as the namespace // except its separated by "/" instead of ".". // In order to find the correct dict entry we need to convert the namespace // into a folder path, and then get the entry where the folder path and name is matching. var infoNameSpaceFolderPath = ConvertNamespaceToDictionaryFolderPath(info.NodeNamespace); var infoCategoryFolderPath = ConvertNamespaceToDictionaryFolderPath(info.FullCategory); // First we try and find a match using the fileInfo directly. // There are cases where a LayoutSpecification is not provided // and therefor we need to find a match from the fileInfo directly. // There are also instances where the particular node/namespace // has not been added to the LayoutSpecification (I think!!) which is // another reason to always try and make the match using the fileInfo directly first. var matchingEntry = dictEntrys .Where(x => x.FolderPath.StartsWith(infoNameSpaceFolderPath) || x.FolderPath.StartsWith(infoCategoryFolderPath) && x.Name == info.NodeName) .FirstOrDefault(); // If we couldn't find a match using the fileInfo directly and a LayoutSpecification is provided // we try and find a match using that. if (matchingEntry is null && spec != null) { if (TryGetMatchingEntryFromLayoutSpec(spec.sections.Cast <LayoutElement>().ToList(), dictEntrys, info, string.Empty, out matchingEntry)) { return(matchingEntry); } } return(matchingEntry); }
/// <summary> /// Trys to find a DictionaryEntry that matches the input MdFileInfo while applying LayoutElement from a LayoutSpecification /// by recursively checking LayoutElements from the LayoutSpecification until a match is found. /// </summary> /// <param name="sections">All Sections coming from a LayoutSpecification</param> /// <param name="dictEntrys">All DictionaryEntrys</param> /// <param name="info">MdFileInfo we are trying to find a DictionaryEntry match for</param> /// <param name="matchingPath">A path matching the DictionaryEntrys path, this path is generated during this recursive function</param> /// <param name="matchingEntry">The DictionaryEntry that matches the MdFileInfo</param> /// <returns></returns> private static bool TryGetMatchingEntryFromLayoutSpec( IEnumerable <LayoutElement> sections, IEnumerable <DynamoDictionaryEntry> dictEntrys, MdFileInfo info, string matchingPath, out DynamoDictionaryEntry matchingEntry) { var nodeNameWithoutArgs = info.NodeName .Split('(') .FirstOrDefault(); matchingEntry = null; foreach (var item in sections) { if (!(item is LayoutSection)) { matchingPath = $"{matchingPath}/{item.text}"; } if (item.include.Count > 0) { // We need to test a lot of options here, the dictionary json paths can come from different places it seems // Mostly when dealing with ZT nodes the full category name works, with NodeModels we need to use the namespace var matchingLayoutInfo = item.include.Where(x => info.FullCategory.StartsWith(x.path) || $"{info.FullCategory}.{nodeNameWithoutArgs}" == x.path || info.NodeNamespace == x.path || info.NodeNamespace.StartsWith(x.path) || $"{info.NodeNamespace}.{nodeNameWithoutArgs}" == x.path) .FirstOrDefault(); if (matchingLayoutInfo != null) { // At this point matchingPath looks something like : "/Display/Color" // therefor we need to remove the first '/' var path = matchingPath.Remove(0, 1); // There are cases where the NodeName has two words separated by a "." // this is true for many of the List nodes in CoreNodeModels, e.g. List.LaceLongest. // The dictionary entry for List.LaceLongest only has LaceLongest in the name // therefor we need to first try and match with the name, and after that try and match with the "List." part removed. var endIndex = info.NodeName.LastIndexOf("."); var nodeNameWithoutPrefix = endIndex > 0 ? info.NodeName.Remove(0, endIndex + 1) : info.NodeName; // First we try and match the folder path that we create in this recursive func with the folder path in the dict entry. // Then we check if either the NodeName on the info object or the nodeNameWithoutPrefix matches the Name from the dict entry. // Lastly we need to make sure that the last part of the infos FullCategory is contained in the folder path of the dict entry. // The last check is needed because the layoutspec does not always match what is in the dictionary json, // heres an example: // When trying to get a matching entry for the node "ByThreePoints" which namespace is // ProtoGeometry.Autodesk.DesignScript.Geometry.Arc, the DynamoCore layout spec puts all // ProtoGeometry.Autodesk.DesignScript.Geometry.Arc under "Curves", which means we get a matchingLayoutInfo // when the matching path is Geometry/Curves, but in the dictionary json // "ByThreePoints" folder path is specified as "Geometry/Curves/Arc/Query" // meaning we are missing the "Arc" part, we can get Arc from the last part of the FullCategory. // If we do not add that part to the matching we might end up with an incorrect entry. var matchingEntries = dictEntrys .Where(x => x.FolderPath.StartsWith(path) && // below regex compares the two string without considering whitespace. // we need this as the Dictionary specify overloaded nodes with args like this: // Max (int1, int2) - adding unnecessary white space between node name and args. // the MdFileInfo will specify the same like this : Max(int1, int2). (Regex.Replace(x.Name, @"\s+", "") == Regex.Replace(info.NodeName, @"\s+", "") || Regex.Replace(x.Name, @"\s+", "") == Regex.Replace(nodeNameWithoutPrefix, @"\s+", ""))); if (matchingEntries.Count() > 1) { matchingEntry = matchingEntries .Where(x => x.FolderPath.Contains(info.FullCategory.Split(new char[] { '.' }).Last())) .FirstOrDefault(); } else { matchingEntry = matchingEntries.FirstOrDefault(); } if (matchingEntry != null) { return(true); } } } if (TryGetMatchingEntryFromLayoutSpec(item.childElements, dictEntrys, info, matchingPath, out matchingEntry)) { return(true); } if (string.IsNullOrEmpty(matchingPath)) { continue; } matchingPath = matchingPath.Substring(0, matchingPath.LastIndexOf('/')); } return(false); }