internal IEnumerable<UserType> AddSymbols(IEnumerable<Symbol> symbols, XmlType type, string nameSpace, UserTypeGenerationFlags generationFlags)
{
if (!type.IsTemplate && symbols.Count() > 1)
throw new Exception("Type has more than one symbol for " + type.Name);
if (!type.IsTemplate)
{
yield return AddSymbol(symbols.First(), type, nameSpace, generationFlags);
}
else
{
// Bucketize template user types based on number of template arguments
var buckets = new Dictionary<int, List<TemplateUserType>>();
foreach (Symbol symbol in symbols)
{
UserType userType = null;
try
{
// We want to ignore "empty" generic classes (for now)
if (symbol.Name == null || symbol.Size == 0)
continue;
// Generate template user type
TemplateUserType templateType = new TemplateUserType(symbol, type, nameSpace, this);
#if false // TODO: Verify if we want to use simple user type instead of template user type
if (templateType.AllTemplateArguments.Count == 0)
{
// Template does not have arguments that can be used by generic
// Make it specialized type
userType = this.AddSymbol(symbol, null, moduleName, generationOptions);
}
else
#endif
{
List<TemplateUserType> templates;
symbol.UserType = templateType;
if (!buckets.TryGetValue(templateType.AllTemplateArguments.Count, out templates))
buckets.Add(templateType.AllTemplateArguments.Count, templates = new List<TemplateUserType>());
templates.Add(templateType);
}
}
catch (Exception ex)
{
// TODO: Verify if we need to add this as specialization
if (ex.Message != "Wrongly formed template argument")
throw;
}
if (userType != null)
yield return userType;
}
// Add newly generated types
foreach (List<TemplateUserType> templatesInBucket in buckets.Values)
{
// TODO: Verify that all templates in the list can be described by the same class (also do check for inner-types)
// Sort Templates by Class Name.
// This removes ambiguity caused by parallel type processing.
//
List<TemplateUserType> templates = templatesInBucket.OrderBy(t => t.Symbol.Name.Count(c => c == '*'))
.ThenBy(t => t.Symbol.Name.Count(c => c == '<'))
.ThenBy(t => t.Symbol.Name).ToList();
// Select best suited type for template
TemplateUserType template = templates.First();
foreach (var specializedTemplate in templates)
{
var arguments = specializedTemplate.AllTemplateArguments;
// Check if all arguments are different
if (arguments.Distinct().Count() == arguments.Count())
{
// Check if all arguments are simple user type
bool simpleUserType = true;
foreach (var argument in arguments)
{
var argumentSymbol = GlobalCache.GetSymbol(argument, specializedTemplate.Module);
if (argumentSymbol.Tag != SymTagEnum.SymTagUDT || argumentSymbol.Name.Contains("<"))
{
simpleUserType = false;
break;
}
}
if (simpleUserType)
{
template = specializedTemplate;
break;
}
// Check if none of the arguments is template user type
bool noneIsTemplate = true;
foreach (var argument in arguments)
{
var argumentSymbol = GlobalCache.GetSymbol(argument, specializedTemplate.Module);
if (argumentSymbol.Tag == SymTagEnum.SymTagUDT && argumentSymbol.Name.Contains("<"))
{
noneIsTemplate = false;
break;
}
}
if (noneIsTemplate)
{
template = specializedTemplate;
continue;
}
}
// This one is as good as any...
}
// Move all types under the selected type
foreach (var specializedTemplate in templates)
{
template.SpecializedTypes.Add(specializedTemplate);
specializedTemplate.TemplateType = template;
}
yield return template;
}
}
}