using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using FastReport.Utils;
using System.Windows.Forms;
namespace FastReport.Data
{
///
/// Obsolete. Specifies a set of flags used to convert business objects into datasources.
///
[Flags]
public enum BOConverterFlags
{
///
/// Specifies no actions.
///
None,
///
/// Allows using the fields of a business object.
///
AllowFields,
///
/// Allows using properties of a business object with BrowsableAttribute only.
///
BrowsableOnly
}
///
/// Specifies a kind of property.
///
public enum PropertyKind
{
///
/// Specifies the property of a simple type (such as integer).
///
Simple,
///
/// Specifies the complex property such as class with own properties.
///
Complex,
///
/// Specifies the property which is a list of objects (is of IEnumerable type).
///
Enumerable
}
internal partial class BusinessObjectConverter
{
private Dictionary dictionary;
private int nestingLevel;
private int maxNestingLevel;
private FastNameCreator nameCreator;
private PropertyKind GetPropertyKind(string name, Type type)
{
if (type == null)
return PropertyKind.Simple;
PropertyKind kind = PropertyKind.Complex;
if (type.IsValueType ||
type == typeof(string) ||
type == typeof(byte[]) ||
typeof(Image).IsAssignableFrom(type))
{
kind = PropertyKind.Simple;
}
else if (typeof(IEnumerable).IsAssignableFrom(type))
{
kind = PropertyKind.Enumerable;
}
GetPropertyKindEventArgs args = new GetPropertyKindEventArgs(name, type, kind);
Config.ReportSettings.OnGetBusinessObjectPropertyKind(null, args);
return args.PropertyKind;
}
private bool IsSimpleType(string name, Type type)
{
return GetPropertyKind(name, type) == PropertyKind.Simple;
}
private bool IsEnumerable(string name, Type type)
{
return GetPropertyKind(name, type) == PropertyKind.Enumerable;
}
private bool IsLoop(Column column, Type type)
{
while (column != null)
{
if (column.DataType == type)
return true;
column = column.Parent as Column;
}
return false;
}
private PropertyDescriptorCollection GetProperties(Column column)
{
using (BindingSource source = new BindingSource())
{
source.DataSource = column.Reference != null ? column.Reference : column.DataType;
// to get properties list of ICustomTypeDescriptor type, we need an instance
object instance = null;
if (source.DataSource is Type &&
typeof(ICustomTypeDescriptor).IsAssignableFrom(source.DataSource as Type))
{
try
{
GetTypeInstanceEventArgs args = new GetTypeInstanceEventArgs(source.DataSource as Type);
Config.ReportSettings.OnGetBusinessObjectTypeInstance(null, args);
instance = args.Instance;
source.DataSource = instance;
}
catch
{
}
}
// generic list? get element type
if (column.Reference == null && column.DataType.IsGenericType)
{
source.DataSource = column.DataType.GetGenericArguments()[0];
}
PropertyDescriptorCollection properties = source.GetItemProperties(null);
PropertyDescriptorCollection filteredProperties = new PropertyDescriptorCollection(null);
foreach (PropertyDescriptor prop in properties)
{
FilterPropertiesEventArgs args = new FilterPropertiesEventArgs(prop);
Config.ReportSettings.OnFilterBusinessObjectProperties(source.DataSource, args);
if (!args.Skip)
filteredProperties.Add(args.Property);
}
if (instance is IDisposable)
{
try
{
(instance as IDisposable).Dispose();
}
catch
{
}
}
return filteredProperties;
}
}
private Column CreateListValueColumn(Column column)
{
Type itemType = ListBindingHelper.GetListItemType(column.DataType);
// find existing column
Column childColumn = column.FindByPropName("Value");
// column not found, create new one with default settings
if (childColumn == null)
{
childColumn = new Column();
childColumn.Name = "Value";
childColumn.Enabled = IsSimpleType(childColumn.Name, itemType);
childColumn.SetBindableControlType(itemType);
column.Columns.Add(childColumn);
}
childColumn.DataType = itemType;
childColumn.PropName = "Value";
childColumn.PropDescriptor = null;
return childColumn;
}
private void GetReference(Column column, Column childColumn)
{
if (!Config.ReportSettings.UsePropValuesToDiscoverBO)
{
childColumn.Reference = null;
return;
}
object obj = null;
if (column is BusinessObjectDataSource)
{
IEnumerable enumerable = column.Reference as IEnumerable;
if (enumerable != null)
{
IEnumerator enumerator = enumerable.GetEnumerator();
while (enumerator.MoveNext())
{
obj = enumerator.Current;
}
}
}
else
{
obj = column.Reference;
}
if (obj != null)
{
try
{
childColumn.Reference = childColumn.PropDescriptor.GetValue(obj);
}
catch
{
childColumn.Reference = null;
}
}
}
private void CreateInitialObjects(Column column)
{
if (nestingLevel >= maxNestingLevel)
return;
nestingLevel++;
PropertyDescriptorCollection properties = GetProperties(column);
foreach (PropertyDescriptor prop in properties)
{
Type type = prop.PropertyType;
bool isSimpleProperty = IsSimpleType(prop.Name, type);
bool isEnumerable = IsEnumerable(prop.Name, type);
Column childColumn = null;
if (isEnumerable)
childColumn = new BusinessObjectDataSource();
else
childColumn = new Column();
column.Columns.Add(childColumn);
childColumn.Name = isEnumerable ? dictionary.CreateUniqueName(prop.Name) : prop.Name;
childColumn.Alias = prop.DisplayName;
childColumn.DataType = type;
childColumn.PropName = prop.Name;
childColumn.PropDescriptor = prop;
childColumn.SetBindableControlType(type);
childColumn.Enabled = !isEnumerable || nestingLevel < maxNestingLevel;
if (!isSimpleProperty)
{
GetReference(column, childColumn);
CreateInitialObjects(childColumn);
}
}
if (IsEnumerable(column.Name, column.DataType) && properties.Count == 0)
{
CreateListValueColumn(column);
}
nestingLevel--;
}
// create initial n-level structure
public void CreateInitialObjects(Column column, int maxNestingLevel)
{
this.maxNestingLevel = maxNestingLevel;
CreateInitialObjects(column);
}
// update existing columns - add new, delete non-existent, update PropDescriptor
public void UpdateExistingObjects(Column column, int maxNestingLevel)
{
this.maxNestingLevel = maxNestingLevel;
nameCreator = new FastNameCreator(dictionary.Report.AllNamedObjects);
UpdateExistingObjects(column);
}
private void UpdateExistingObjects(Column column)
{
nestingLevel++;
// reset property descriptors to determine later which columns are outdated
foreach (Column c in column.Columns)
{
c.PropDescriptor = null;
}
PropertyDescriptorCollection properties = GetProperties(column);
if (properties.Count > 0)
{
foreach (PropertyDescriptor prop in properties)
{
Type type = prop.PropertyType;
bool isSimpleProperty = IsSimpleType(prop.Name, type);
bool isEnumerable = IsEnumerable(prop.Name, type);
// find existing column
Column childColumn = column.FindByPropName(prop.Name);
// column not found, create new one
if (childColumn == null)
{
if (isEnumerable)
childColumn = new BusinessObjectDataSource();
else
childColumn = new Column();
column.Columns.Add(childColumn);
if (isEnumerable)
nameCreator.CreateUniqueName(childColumn);
else
childColumn.Name = prop.Name;
childColumn.Alias = prop.DisplayName;
childColumn.SetBindableControlType(type);
// enable column if it is simple property, or max nesting level is not reached
childColumn.Enabled = isSimpleProperty || nestingLevel < maxNestingLevel;
}
// update column's prop data - the schema may be changed
childColumn.DataType = prop.PropertyType;
childColumn.PropName = prop.Name;
childColumn.PropDescriptor = prop;
if (childColumn.Enabled && !isSimpleProperty)
{
GetReference(column, childColumn);
UpdateExistingObjects(childColumn);
}
}
// remove non-existent columns
for (int i = 0; i < column.Columns.Count; i++)
{
Column c = column.Columns[i];
// delete columns with empty descriptors, except the "Value" columns
if (c.PropDescriptor == null && c.PropName != "Value")
{
column.Columns.RemoveAt(i);
i--;
}
}
}
else if (IsEnumerable(column.Name, column.DataType))
{
CreateListValueColumn(column);
}
nestingLevel--;
}
public BusinessObjectConverter(Dictionary dictionary)
{
this.dictionary = dictionary;
}
}
}