1 //
2 // Copyright (c) 2010-2023 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Collections.Generic;
9 using System.Linq;
10 using Antmicro.Renode.Logging;
11 
12 namespace Antmicro.Renode.PlatformDescription.Syntax
13 {
14     public static class SyntaxTreeHelpers
15     {
ScanFor(object objectToScan, IWithPosition objectToFind)16         public static bool ScanFor(object objectToScan, IWithPosition objectToFind)
17         {
18             var found = false;
19             VisitSyntaxTree<IWithPosition>(objectToScan, x =>
20             {
21                 found |= objectToFind.Equals(x);
22             });
23             return found;
24         }
25 
26         public static void VisitSyntaxTree<TValue>(object root, Action<TValue> visitorAction) where TValue : class
27         {
VisitSyntaxTreeInner(root, visitorAction, null)28             VisitSyntaxTreeInner<TValue>(root, visitorAction, null);
29         }
30 
31         public static void VisitSyntaxTree<TValue>(object root, Action<TValue> visitorAction, Func<object, bool, bool> filter) where TValue : class
32         {
VisitSyntaxTreeInner(root, visitorAction, filter)33             VisitSyntaxTreeInner(root, visitorAction, filter);
34         }
35 
36         private static void VisitSyntaxTreeInner<TValue>(object objectToVisit, Action<TValue> visitorAction, Func<object, bool, bool> filter) where TValue : class
37         {
38             var objectAsValue = objectToVisit as TValue;
39             if(objectAsValue != null)
40             {
41                 visitorAction(objectAsValue);
42             }
43 
44             var objectIsEntry = objectToVisit.GetType() == typeof(Antmicro.Renode.PlatformDescription.Syntax.Entry);
45 
46             var objectAsVisitable = objectToVisit as IVisitable;
47             if(objectAsVisitable != null)
48             {
49                 foreach(var element in objectAsVisitable.Visit())
50                 {
51                     if(element != null && ApplyFilter(element, objectIsEntry, filter))
52                     {
53                         VisitSyntaxTreeInner(element, visitorAction, filter);
54                     }
55                 }
56                 return;
57             }
58 
59             var objectType = objectToVisit.GetType();
60             var publicProperties = objectType.GetProperties();
61             foreach(var property in publicProperties.Where(x => x.CanRead))
62             {
63                 var propertyValue = property.GetGetMethod().Invoke(objectToVisit, new object[0]);
64                 if(propertyValue != null)
65                 {
66                     var typeOfValue = propertyValue.GetType();
67                     if(typeOfValue.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IEnumerable<>) &&
68                                                        x.GetGenericArguments()[0].Namespace.StartsWith(OurNamespace, StringComparison.InvariantCulture)))
69                     {
70                         foreach(var element in ((System.Collections.IEnumerable)propertyValue))
71                         {
72                             if(ApplyFilter(element, objectIsEntry, filter))
73                             {
74                                 VisitSyntaxTreeInner(element, visitorAction, filter);
75                             }
76                         }
77                     }
78                     else if(typeOfValue.Namespace.StartsWith(OurNamespace, StringComparison.InvariantCulture))
79                     {
80                         if(ApplyFilter(propertyValue, objectIsEntry, filter))
81                         {
82                             VisitSyntaxTreeInner(propertyValue, visitorAction, filter);
83                         }
84                     }
85                 }
86             }
87         }
88 
ApplyFilter(object obj, bool isEntryChild, Func<object, bool, bool> filter)89         private static bool ApplyFilter(object obj, bool isEntryChild, Func<object, bool, bool> filter)
90         {
91             return filter == null ? true : filter(obj, isEntryChild);
92         }
93 
94         private static readonly string OurNamespace = typeof(SyntaxTreeHelpers).Namespace;
95     }
96 }
97