BusinessObjectConverter.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. using System;
  2. using System.Collections;
  3. using System.ComponentModel;
  4. using System.Drawing;
  5. using FastReport.Utils;
  6. using System.Windows.Forms;
  7. namespace FastReport.Data
  8. {
  9. /// <summary>
  10. /// <b>Obsolete</b>. Specifies a set of flags used to convert business objects into datasources.
  11. /// </summary>
  12. [Flags]
  13. public enum BOConverterFlags
  14. {
  15. /// <summary>
  16. /// Specifies no actions.
  17. /// </summary>
  18. None,
  19. /// <summary>
  20. /// Allows using the fields of a business object.
  21. /// </summary>
  22. AllowFields,
  23. /// <summary>
  24. /// Allows using properties of a business object with <b>BrowsableAttribute</b> only.
  25. /// </summary>
  26. BrowsableOnly
  27. }
  28. /// <summary>
  29. /// Specifies a kind of property.
  30. /// </summary>
  31. public enum PropertyKind
  32. {
  33. /// <summary>
  34. /// Specifies the property of a simple type (such as integer).
  35. /// </summary>
  36. Simple,
  37. /// <summary>
  38. /// Specifies the complex property such as class with own properties.
  39. /// </summary>
  40. Complex,
  41. /// <summary>
  42. /// Specifies the property which is a list of objects (is of IEnumerable type).
  43. /// </summary>
  44. Enumerable
  45. }
  46. internal partial class BusinessObjectConverter
  47. {
  48. private Dictionary dictionary;
  49. private int nestingLevel;
  50. private int maxNestingLevel;
  51. private FastNameCreator nameCreator;
  52. private PropertyKind GetPropertyKind(string name, Type type)
  53. {
  54. if (type == null)
  55. return PropertyKind.Simple;
  56. PropertyKind kind = PropertyKind.Complex;
  57. if (type.IsValueType ||
  58. type == typeof(string) ||
  59. type == typeof(byte[]) ||
  60. typeof(Image).IsAssignableFrom(type))
  61. {
  62. kind = PropertyKind.Simple;
  63. }
  64. else if (typeof(IEnumerable).IsAssignableFrom(type))
  65. {
  66. kind = PropertyKind.Enumerable;
  67. }
  68. GetPropertyKindEventArgs args = new GetPropertyKindEventArgs(name, type, kind);
  69. Config.ReportSettings.OnGetBusinessObjectPropertyKind(null, args);
  70. return args.PropertyKind;
  71. }
  72. private bool IsSimpleType(string name, Type type)
  73. {
  74. return GetPropertyKind(name, type) == PropertyKind.Simple;
  75. }
  76. private bool IsEnumerable(string name, Type type)
  77. {
  78. return GetPropertyKind(name, type) == PropertyKind.Enumerable;
  79. }
  80. private bool IsLoop(Column column, Type type)
  81. {
  82. while (column != null)
  83. {
  84. if (column.DataType == type)
  85. return true;
  86. column = column.Parent as Column;
  87. }
  88. return false;
  89. }
  90. private PropertyDescriptorCollection GetProperties(Column column)
  91. {
  92. using (BindingSource source = new BindingSource())
  93. {
  94. source.DataSource = column.Reference != null ? column.Reference : column.DataType;
  95. // to get properties list of ICustomTypeDescriptor type, we need an instance
  96. object instance = null;
  97. if (source.DataSource is Type &&
  98. typeof(ICustomTypeDescriptor).IsAssignableFrom(source.DataSource as Type))
  99. {
  100. try
  101. {
  102. GetTypeInstanceEventArgs args = new GetTypeInstanceEventArgs(source.DataSource as Type);
  103. Config.ReportSettings.OnGetBusinessObjectTypeInstance(null, args);
  104. instance = args.Instance;
  105. source.DataSource = instance;
  106. }
  107. catch
  108. {
  109. }
  110. }
  111. // generic list? get element type
  112. if (column.Reference == null && column.DataType.IsGenericType)
  113. {
  114. source.DataSource = column.DataType.GetGenericArguments()[0];
  115. }
  116. PropertyDescriptorCollection properties = source.GetItemProperties(null);
  117. PropertyDescriptorCollection filteredProperties = new PropertyDescriptorCollection(null);
  118. foreach (PropertyDescriptor prop in properties)
  119. {
  120. FilterPropertiesEventArgs args = new FilterPropertiesEventArgs(prop);
  121. Config.ReportSettings.OnFilterBusinessObjectProperties(source.DataSource, args);
  122. if (!args.Skip)
  123. filteredProperties.Add(args.Property);
  124. }
  125. if (instance is IDisposable)
  126. {
  127. try
  128. {
  129. (instance as IDisposable).Dispose();
  130. }
  131. catch
  132. {
  133. }
  134. }
  135. return filteredProperties;
  136. }
  137. }
  138. private Column CreateListValueColumn(Column column)
  139. {
  140. Type itemType = ListBindingHelper.GetListItemType(column.DataType);
  141. // find existing column
  142. Column childColumn = column.FindByPropName("Value");
  143. // column not found, create new one with default settings
  144. if (childColumn == null)
  145. {
  146. childColumn = new Column();
  147. childColumn.Name = "Value";
  148. childColumn.Enabled = IsSimpleType(childColumn.Name, itemType);
  149. childColumn.SetBindableControlType(itemType);
  150. column.Columns.Add(childColumn);
  151. }
  152. childColumn.DataType = itemType;
  153. childColumn.PropName = "Value";
  154. childColumn.PropDescriptor = null;
  155. return childColumn;
  156. }
  157. private void GetReference(Column column, Column childColumn)
  158. {
  159. if (!Config.ReportSettings.UsePropValuesToDiscoverBO)
  160. {
  161. childColumn.Reference = null;
  162. return;
  163. }
  164. object obj = null;
  165. if (column is BusinessObjectDataSource)
  166. {
  167. IEnumerable enumerable = column.Reference as IEnumerable;
  168. if (enumerable != null)
  169. {
  170. IEnumerator enumerator = enumerable.GetEnumerator();
  171. while (enumerator.MoveNext())
  172. {
  173. obj = enumerator.Current;
  174. }
  175. }
  176. }
  177. else
  178. {
  179. obj = column.Reference;
  180. }
  181. if (obj != null)
  182. {
  183. try
  184. {
  185. childColumn.Reference = childColumn.PropDescriptor.GetValue(obj);
  186. }
  187. catch
  188. {
  189. childColumn.Reference = null;
  190. }
  191. }
  192. }
  193. private void CreateInitialObjects(Column column)
  194. {
  195. if (nestingLevel >= maxNestingLevel)
  196. return;
  197. nestingLevel++;
  198. PropertyDescriptorCollection properties = GetProperties(column);
  199. foreach (PropertyDescriptor prop in properties)
  200. {
  201. Type type = prop.PropertyType;
  202. bool isSimpleProperty = IsSimpleType(prop.Name, type);
  203. bool isEnumerable = IsEnumerable(prop.Name, type);
  204. Column childColumn = null;
  205. if (isEnumerable)
  206. childColumn = new BusinessObjectDataSource();
  207. else
  208. childColumn = new Column();
  209. column.Columns.Add(childColumn);
  210. childColumn.Name = isEnumerable ? dictionary.CreateUniqueName(prop.Name) : prop.Name;
  211. childColumn.Alias = prop.DisplayName;
  212. childColumn.DataType = type;
  213. childColumn.PropName = prop.Name;
  214. childColumn.PropDescriptor = prop;
  215. childColumn.SetBindableControlType(type);
  216. childColumn.Enabled = !isEnumerable || nestingLevel < maxNestingLevel;
  217. if (!isSimpleProperty)
  218. {
  219. GetReference(column, childColumn);
  220. CreateInitialObjects(childColumn);
  221. }
  222. }
  223. if (IsEnumerable(column.Name, column.DataType) && properties.Count == 0)
  224. {
  225. CreateListValueColumn(column);
  226. }
  227. nestingLevel--;
  228. }
  229. // create initial n-level structure
  230. public void CreateInitialObjects(Column column, int maxNestingLevel)
  231. {
  232. this.maxNestingLevel = maxNestingLevel;
  233. CreateInitialObjects(column);
  234. }
  235. // update existing columns - add new, delete non-existent, update PropDescriptor
  236. public void UpdateExistingObjects(Column column, int maxNestingLevel)
  237. {
  238. this.maxNestingLevel = maxNestingLevel;
  239. nameCreator = new FastNameCreator(dictionary.Report.AllNamedObjects);
  240. UpdateExistingObjects(column);
  241. }
  242. private void UpdateExistingObjects(Column column)
  243. {
  244. nestingLevel++;
  245. // reset property descriptors to determine later which columns are outdated
  246. foreach (Column c in column.Columns)
  247. {
  248. c.PropDescriptor = null;
  249. }
  250. PropertyDescriptorCollection properties = GetProperties(column);
  251. if (properties.Count > 0)
  252. {
  253. foreach (PropertyDescriptor prop in properties)
  254. {
  255. Type type = prop.PropertyType;
  256. bool isSimpleProperty = IsSimpleType(prop.Name, type);
  257. bool isEnumerable = IsEnumerable(prop.Name, type);
  258. // find existing column
  259. Column childColumn = column.FindByPropName(prop.Name);
  260. // column not found, create new one
  261. if (childColumn == null)
  262. {
  263. if (isEnumerable)
  264. childColumn = new BusinessObjectDataSource();
  265. else
  266. childColumn = new Column();
  267. column.Columns.Add(childColumn);
  268. if (isEnumerable)
  269. nameCreator.CreateUniqueName(childColumn);
  270. else
  271. childColumn.Name = prop.Name;
  272. childColumn.Alias = prop.DisplayName;
  273. childColumn.SetBindableControlType(type);
  274. // enable column if it is simple property, or max nesting level is not reached
  275. childColumn.Enabled = isSimpleProperty || nestingLevel < maxNestingLevel;
  276. }
  277. // update column's prop data - the schema may be changed
  278. childColumn.DataType = prop.PropertyType;
  279. childColumn.PropName = prop.Name;
  280. childColumn.PropDescriptor = prop;
  281. if (childColumn.Enabled && !isSimpleProperty)
  282. {
  283. GetReference(column, childColumn);
  284. UpdateExistingObjects(childColumn);
  285. }
  286. }
  287. // remove non-existent columns
  288. for (int i = 0; i < column.Columns.Count; i++)
  289. {
  290. Column c = column.Columns[i];
  291. // delete columns with empty descriptors, except the "Value" columns
  292. if (c.PropDescriptor == null && c.PropName != "Value")
  293. {
  294. column.Columns.RemoveAt(i);
  295. i--;
  296. }
  297. }
  298. }
  299. else if (IsEnumerable(column.Name, column.DataType))
  300. {
  301. CreateListValueColumn(column);
  302. }
  303. nestingLevel--;
  304. }
  305. public BusinessObjectConverter(Dictionary dictionary)
  306. {
  307. this.dictionary = dictionary;
  308. }
  309. }
  310. }