Lean  $LEAN_TAG$
PandasData.DataTypeMember.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using Python.Runtime;
18 using System;
19 using System.Collections.Generic;
20 using System.Reflection;
21 using System.Text;
22 
23 namespace QuantConnect.Python
24 {
25  public partial class PandasData
26  {
27  private static DataTypeMember CreateDataTypeMember(MemberInfo member, DataTypeMember[] children = null)
28  {
29  return member switch
30  {
31  PropertyInfo property => new PropertyMember(property, children),
32  FieldInfo field => new FieldMember(field, children),
33  _ => throw new ArgumentException($"Member type {member.MemberType} is not supported")
34  };
35  }
36 
37  /// <summary>
38  /// Represents a member of a data type, either a property or a field and it's children members in case it's a complex type.
39  /// It contains logic to get the member name and the children names, taking into account the parent prefixes.
40  /// </summary>
41  private abstract class DataTypeMember
42  {
43  private static readonly StringBuilder _stringBuilder = new StringBuilder();
44 
45  private DataTypeMember _parent;
46  private string _name;
47 
48  public MemberInfo Member { get; }
49 
50  public DataTypeMember[] Children { get; }
51 
52  public abstract bool IsProperty { get; }
53 
54  public abstract bool IsField { get; }
55 
56  /// <summary>
57  /// The prefix to be used for the children members when a class being expanded has multiple properties/fields of the same type
58  /// </summary>
59  public string Prefix { get; private set; }
60 
61  public bool ShouldBeUnwrapped => Children != null && Children.Length > 0;
62 
63  /// <summary>
64  /// Whether this member is Tick.LastPrice or OpenInterest.LastPrice.
65  /// Saved to avoid MemberInfo comparisons in the future
66  /// </summary>
67  public bool IsTickLastPrice { get; }
68 
69  public bool IsTickProperty { get; }
70 
71  public DataTypeMember(MemberInfo member, DataTypeMember[] children = null)
72  {
73  Member = member;
74  Children = children;
75 
76  IsTickLastPrice = member == _tickLastPriceMember || member == _openInterestLastPriceMember;
77  IsTickProperty = IsProperty && member.DeclaringType == typeof(Tick);
78 
79  if (Children != null)
80  {
81  foreach (var child in Children)
82  {
83  child._parent = this;
84  }
85  }
86  }
87 
88  public void SetPrefix()
89  {
90  Prefix = Member.Name.ToLowerInvariant();
91  }
92 
93  /// <summary>
94  /// Gets the member name, adding the parent prefixes if necessary.
95  /// </summary>
96  /// <param name="customName">If passed, it will be used instead of the <see cref="Member"/>'s name</param>
97  public string GetMemberName(string customName = null)
98  {
99  if (ShouldBeUnwrapped)
100  {
101  return string.Empty;
102  }
103 
104  if (!string.IsNullOrEmpty(customName))
105  {
106  return BuildMemberName(customName);
107  }
108 
109  if (string.IsNullOrEmpty(_name))
110  {
111  _name = BuildMemberName(GetBaseName());
112  }
113 
114  return _name;
115  }
116 
117  public IEnumerable<string> GetMemberNames()
118  {
119  return GetMemberNames(null);
120  }
121 
122  public abstract object GetValue(object instance);
123 
124  public abstract Type GetMemberType();
125 
126  public override string ToString()
127  {
128  return $"{GetMemberType().Name} {Member.Name}";
129  }
130 
131  private string BuildMemberName(string baseName)
132  {
133  _stringBuilder.Clear();
134  while (_parent != null && _parent.ShouldBeUnwrapped)
135  {
136  _stringBuilder.Insert(0, _parent.Prefix);
137  _parent = _parent._parent;
138  }
139 
140  _stringBuilder.Append(baseName.ToLowerInvariant());
141  return _stringBuilder.ToString();
142  }
143 
144  private IEnumerable<string> GetMemberNames(string parentPrefix)
145  {
146  // If there are no children, return the name of the member. Else ignore the member and return the children names
147  if (ShouldBeUnwrapped)
148  {
149  var prefix = parentPrefix ?? string.Empty;
150  if (!string.IsNullOrEmpty(Prefix))
151  {
152  prefix += Prefix;
153  }
154 
155  foreach (var child in Children)
156  {
157  foreach (var childName in child.GetMemberNames(prefix))
158  {
159  yield return childName;
160  }
161  }
162  yield break;
163  }
164 
165  var memberName = GetBaseName();
166  _name = string.IsNullOrEmpty(parentPrefix) ? memberName : $"{parentPrefix}{memberName}";
167  yield return _name;
168  }
169 
170  private string GetBaseName()
171  {
172  var baseName = Member.GetCustomAttribute<PandasColumnAttribute>()?.Name;
173  if (string.IsNullOrEmpty(baseName))
174  {
175  baseName = Member.Name;
176  }
177 
178  return baseName.ToLowerInvariant();
179  }
180  }
181 
182  private class PropertyMember : DataTypeMember
183  {
184  private PropertyInfo _property;
185 
186  public override bool IsProperty => true;
187 
188  public override bool IsField => false;
189 
190  public PropertyMember(PropertyInfo property, DataTypeMember[] children = null)
191  : base(property, children)
192  {
193  _property = property;
194  }
195 
196  public override object GetValue(object instance)
197  {
198  return _property.GetValue(instance);
199  }
200 
201  public override Type GetMemberType()
202  {
203  return _property.PropertyType;
204  }
205  }
206 
207  private class FieldMember : DataTypeMember
208  {
209  private FieldInfo _field;
210 
211  public override bool IsProperty => false;
212 
213  public override bool IsField => true;
214 
215  public FieldMember(FieldInfo field, DataTypeMember[] children = null)
216  : base(field, children)
217  {
218  _field = field;
219  }
220 
221  public override object GetValue(object instance)
222  {
223  return _field.GetValue(instance);
224  }
225 
226  public override Type GetMemberType()
227  {
228  return _field.FieldType;
229  }
230  }
231  }
232 }