using System;
using System.Reflection;
using System.Collections.Generic;
namespace Light.Model
{
/// <summary>
/// Factory class that builds table models based on class types.
/// </summary>
public static class TableFactory
{
private const BindingFlags FLAGS = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
private const BindingFlags FLAGS_2 = FLAGS | BindingFlags.DeclaredOnly;
/// <summary>
/// Builds a table model for a given type.
/// </summary>
/// <param name="type">type for which to build a table model</param>
/// <returns>table model for a given type</returns>
public static Table Build(Type type)
{
// Check for TableRef attribute - maybe we need to look at another type.
if(type.IsDefined(typeof(TableRefAttribute), false))
{
TableRefAttribute tableref = (TableRefAttribute) type.GetCustomAttributes(typeof(TableRefAttribute), false)[0];
return Build(tableref.RefType);
}
// Get the table information.
if(!type.IsDefined(typeof(TableAttribute), false))
throw new DeclarationException("Type " + type.Name + " does not have the Table attribute.");
TableAttribute tableAttr = (TableAttribute) type.GetCustomAttributes(typeof(TableAttribute), false)[0];
// TableAttribute can be without a name, so take the name of the type.
if(string.IsNullOrEmpty(tableAttr.Name))
tableAttr.Name = type.Name;
List<AbstractColumn> columns = new List<AbstractColumn>();
List<Trigger> triggers = new List<Trigger>();
// Process properties, fields, and methods of given type and base types, if needed.
Type currentType = type;
TableAttribute currentTable = tableAttr;
do
{
ProcessFields(currentType, columns);
ProcessProperties(currentType, columns);
ProcessMappings(currentType, columns);
ProcessTriggers(currentType, triggers);
if(currentTable == null || !currentTable.Inherit)
break;
currentType = currentType.BaseType;
while(currentType.IsDefined(typeof(TableRefAttribute), false))
currentType = ((TableRefAttribute) currentType.GetCustomAttributes(
typeof(TableRefAttribute), false)[0]).RefType;
if(currentType.IsDefined(typeof(TableAttribute), false))
currentTable = (TableAttribute) currentType.GetCustomAttributes(typeof(TableAttribute), false)[0];
else
currentTable = null;
}
while(true);
// Create a table and return it.
return new Table(tableAttr.Name, tableAttr.Schema, tableAttr.Sequence,
columns.ToArray(), triggers.ToArray(), tableAttr.ReadOnly);
}
/// <summary>
/// Adds columns defined on properties in given type to given list.
/// </summary>
/// <param name="type">object type</param>
/// <param name="list">column list</param>
private static void ProcessProperties(Type type, List<AbstractColumn> list)
{
object[] array = null;
PropertyInfo[] properties = type.GetProperties(FLAGS_2);
foreach(PropertyInfo p in properties)
{
array = p.GetCustomAttributes(typeof(ColumnAttribute), false);
if(array.Length == 0)
continue;
ColumnAttribute columnAttr = (ColumnAttribute) array[0];
if(string.IsNullOrEmpty(columnAttr.Name))
columnAttr.Name = p.Name;
list.Add(
new PropertyColumn(p, columnAttr.Name, columnAttr.DBType,
columnAttr.Size, columnAttr.Precision, columnAttr.Scale,
columnAttr.PrimaryKey, columnAttr.AutoIncrement)
);
}
}
/// <summary>
/// Adds columns defined on fields in given type to given list.
/// </summary>
/// <param name="type">object type</param>
/// <param name="list">column list</param>
private static void ProcessFields(Type type, List<AbstractColumn> list)
{
object[] array = null;
FieldInfo[] fields = type.GetFields(FLAGS_2);
foreach(FieldInfo f in fields)
{
array = f.GetCustomAttributes(typeof(ColumnAttribute), false);
if(array.Length == 0)
continue;
ColumnAttribute columnAttr = (ColumnAttribute) array[0];
if(string.IsNullOrEmpty(columnAttr.Name))
columnAttr.Name = f.Name;
list.Add(
new FieldColumn(f, columnAttr.Name, columnAttr.DBType,
columnAttr.Size, columnAttr.Precision, columnAttr.Scale,
columnAttr.PrimaryKey, columnAttr.AutoIncrement)
);
}
}
/// <summary>
/// Adds triggers defined in given type to given list.
/// </summary>
/// <param name="type">object type</param>
/// <param name="list">list of triggers</param>
private static void ProcessTriggers(Type type, List<Trigger> list)
{
object[] array = null;
MethodInfo[] methods = type.GetMethods(FLAGS_2);
foreach(MethodInfo method in methods)
{
array = method.GetCustomAttributes(typeof(TriggerAttribute), false);
if(array.Length == 0)
continue;
TriggerAttribute trigAttr = (TriggerAttribute) array[0];
Trigger trigger = new Trigger(method, trigAttr.Action);
list.Add(trigger);
}
}
/// <summary>
/// Adds columns from MappingAttributes defined on a type to given list.
/// </summary>
/// <param name="type">type to check for MappingAttributes</param>
/// <param name="list">list of columns</param>
private static void ProcessMappings(Type type, List<AbstractColumn> list)
{
object[] array = type.GetCustomAttributes(typeof(MappingAttribute), false);
foreach(MappingAttribute ma in array)
{
AbstractColumn column = null;
// We are going to assume that if the name begins with a capital letter
// then it is a property, otherwise it is a field.
if(char.IsUpper(ma.MemberName[0])) {
PropertyInfo p = type.GetProperty(ma.MemberName, FLAGS);
if(p != null) {
if(string.IsNullOrEmpty(ma.Name))
ma.Name = p.Name;
column = new PropertyColumn(p, ma.Name, ma.DBType, ma.Size, ma.Precision, ma.Scale,
ma.PrimaryKey, ma.AutoIncrement);
}
else {
FieldInfo f = type.GetField(ma.MemberName, FLAGS);
if(f != null) {
if(string.IsNullOrEmpty(ma.Name))
ma.Name = f.Name;
column = new FieldColumn(f, ma.Name, ma.DBType, ma.Size, ma.Precision, ma.Scale,
ma.PrimaryKey, ma.AutoIncrement);
}
}
}
else {
FieldInfo f = type.GetField(ma.MemberName, FLAGS);
if(f != null) {
if(string.IsNullOrEmpty(ma.Name))
ma.Name = f.Name;
column = new FieldColumn(f, ma.Name, ma.DBType, ma.Size, ma.Precision, ma.Scale,
ma.PrimaryKey, ma.AutoIncrement);
}
else {
PropertyInfo p = type.GetProperty(ma.MemberName, FLAGS);
if(p != null) {
if(string.IsNullOrEmpty(ma.Name))
ma.Name = p.Name;
column = new PropertyColumn(p, ma.Name, ma.DBType, ma.Size, ma.Precision, ma.Scale,
ma.PrimaryKey, ma.AutoIncrement);
}
}
}
if(column != null)
list.Add(column);
else
throw new DeclarationException("Type " + type.Name + " has a Mapping for an invalid member.");
}
}
}
}