Opentk/Source/Bind/Structures/Enum.cs
the_fiddler 3ec5303e37 Added support for enum overrides.
Enabled enum overrides in the CL and ES generators.
Function parameters now follow enum overrides (for example, if function Foo takes enum Bar and enum Bar is overriden to Baz, this change will be reflected on function Foo).
Changed default CLGenerator settings to not generate debug helpers.
2009-08-11 14:12:20 +00:00

389 lines
13 KiB
C#

#region --- License ---
/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos
* See license.txt for license info
*/
#endregion
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Globalization;
using System.Xml.XPath;
namespace Bind.Structures
{
#region class Enum
public class Enum
{
internal static EnumCollection GLEnums = new EnumCollection();
internal static EnumCollection AuxEnums = new EnumCollection();
static StringBuilder translator = new StringBuilder();
string _name;
static bool enumsLoaded;
#region Initialize
internal static void Initialize(string enumFile, string enumextFile, string auxFile)
{
Initialize(enumFile, enumextFile);
if (!String.IsNullOrEmpty(auxFile))
using (System.IO.StreamReader sr = new System.IO.StreamReader(Path.Combine(Settings.InputPath, auxFile)))
{
AuxEnums = Bind.MainClass.Generator.ReadEnums(sr);
}
}
internal static void Initialize(string enumFile, string enumextFile)
{
if (!enumsLoaded)
{
if (!String.IsNullOrEmpty(enumFile))
{
using (StreamReader sr = Utilities.OpenSpecFile(Settings.InputPath, enumFile))
{
GLEnums = Bind.MainClass.Generator.ReadEnums(sr);
}
}
if (!String.IsNullOrEmpty(enumextFile))
{
using (StreamReader sr = Utilities.OpenSpecFile(Settings.InputPath, enumextFile))
{
foreach (Bind.Structures.Enum e in Bind.MainClass.Generator.ReadEnums(sr).Values)
{
//enums.Add(e.Name, e);
Utilities.Merge(GLEnums, e);
}
}
}
enumsLoaded = true;
}
}
#endregion
#region Constructors
public Enum()
{ }
public Enum(string name)
{
Name = name;
}
#endregion
#region Public Members
// Returns true if the enum contains a collection of flags, i.e. 1, 2, 4, 8, ...
public bool IsFlagCollection
{
get
{
// It seems that all flag collections contain "Mask" in their names.
// This looks like a heuristic, but it holds 100% in practice
// (checked all enums to make sure).
return Name.Contains("Mask");
}
}
#region public string Name
public string Name
{
get { return _name; }
set { _name = value; }
}
#endregion
#region ConstantCollection
Dictionary<string, Constant> _constant_collection = new Dictionary<string, Constant>();
public IDictionary<string, Constant> ConstantCollection
{
get { return _constant_collection; }
}
#endregion
#region TranslateName
public static string TranslateName(string name)
{
if (String.IsNullOrEmpty(name))
return name;
if (Utilities.Keywords.Contains(name))
return name;
translator.Remove(0, translator.Length); // Trick to avoid allocating a new StringBuilder.
// Translate the constant's name to match .Net naming conventions
if ((Settings.Compatibility & Settings.Legacy.NoAdvancedEnumProcessing) == Settings.Legacy.None)
{
bool is_after_underscore_or_number = true; // Detect if we just passed a '_' or a number and make the next char
// uppercase.
bool is_previous_uppercase = false; // Detect if previous character was uppercase, and turn
// the current one to lowercase.
foreach (char c in name)
{
char char_to_add;
if (c == '_' || c == '-')
is_after_underscore_or_number = true;
else
{
if (Char.IsDigit(c))
is_after_underscore_or_number = true;
char_to_add = is_after_underscore_or_number ? Char.ToUpper(c) :
is_previous_uppercase ? Char.ToLower(c) : c;
is_previous_uppercase = Char.IsUpper(c);
translator.Append(char_to_add);
is_after_underscore_or_number = false;
}
}
translator[0] = Char.ToUpper(translator[0]);
}
else
translator.Append(name);
translator.Replace("Pname", "PName");
translator.Replace("SRgb", "Srgb");
string ret = translator.ToString();
if (ret.StartsWith(Settings.EnumPrefix))
return ret.Substring(Settings.EnumPrefix.Length);
return ret;
}
#endregion
#region ToString
public override string ToString()
{
StringBuilder sb = new StringBuilder();
List<Constant> constants = new List<Constant>(ConstantCollection.Values);
constants.Sort(delegate(Constant c1, Constant c2)
{
int ret = String.Compare(c1.Value, c2.Value);
if (ret == 0)
return String.Compare(c1.Name, c2.Name);
return ret;
});
if (IsFlagCollection)
sb.AppendLine("[Flags]");
sb.AppendLine("public enum " + Name);
sb.AppendLine("{");
foreach (Constant c in constants)
{
sb.Append(" ");
sb.Append(c.ToString());
if (!String.IsNullOrEmpty(c.ToString()))
sb.AppendLine(",");
}
sb.Append("}");
return sb.ToString();
}
#endregion
#endregion
}
#endregion
#region class EnumCollection
public class EnumCollection : SortedDictionary<string, Enum>
{
internal void AddRange(EnumCollection enums)
{
foreach (Enum e in enums.Values)
Utilities.Merge(this, e);
}
internal void Translate(XPathDocument overrides)
{
if (overrides == null)
throw new ArgumentNullException("overrides");
string path = "/overrides/enum[@name='{0}']";
// Translate enum names.
{
List<string> keys_to_update = new List<string>();
foreach (Enum e in this.Values)
{
string name = e.Name;
XPathNavigator enum_override = overrides.CreateNavigator().SelectSingleNode(String.Format(path, name));
if (enum_override != null)
{
XPathNavigator name_override = enum_override.SelectSingleNode("name");
if (name_override != null)
{
name = name_override.Value;
}
}
name = Enum.TranslateName(name);
if (name != e.Name)
{
keys_to_update.Add(e.Name);
e.Name = name;
}
}
foreach (string name in keys_to_update)
{
Enum e = this[name];
this.Remove(name);
this.Add(e.Name, e);
}
keys_to_update = null;
}
foreach (Enum e in this.Values)
{
XPathNavigator enum_override = overrides.CreateNavigator().SelectSingleNode(String.Format(path, e.Name));
foreach (Constant c in e.ConstantCollection.Values)
{
if (enum_override != null)
{
XPathNavigator constant_override = enum_override.SelectSingleNode(String.Format("token[@name='{0}']", c.PreviousName)) ??
enum_override.SelectSingleNode(String.Format("token[@name={0}]", c.Name));
if (constant_override != null)
{
foreach (XPathNavigator node in constant_override.SelectChildren(XPathNodeType.Element))
{
switch (node.Name)
{
case "name": c.Name = (string)node.TypedValue; break;
case "value": c.Value = (string)node.TypedValue; break;
}
}
}
}
// There are cases when a value is an aliased constant, with no enum specified.
// (e.g. FOG_COORD_ARRAY_TYPE = GL_FOG_COORDINATE_ARRAY_TYPE)
// In this case try searching all enums for the correct constant to alias (stupid opengl specs).
if (String.IsNullOrEmpty(c.Reference) && !Char.IsDigit(c.Value[0]))
{
foreach (Enum @enum in this.Values)
{
// Skip generic GLenum
if (@enum.Name == "GLenum")
continue;
if (@enum.ConstantCollection.ContainsKey(c.Value))
{
c.Reference = @enum.Name;
break;
}
}
}
}
}
foreach (Enum e in this.Values)
{
restart:
foreach (Constant c in e.ConstantCollection.Values)
{
bool result = Constant.TranslateConstantWithReference(c, Enum.GLEnums, Enum.AuxEnums);
if (!result)
{
e.ConstantCollection.Remove(c.Name);
goto restart;
}
}
}
if (Settings.DropMultipleTokens)
{
// When there are multiple tokens with the same value but different extension
// drop the duplicates. Order of preference: core > ARB > EXT > vendor specific
List<Constant> removed_tokens = new List<Constant>();
foreach (Enum e in this.Values)
{
if (e.Name == "All")
continue;
// This implementation is a not very bright O(n^2).
foreach (Constant c in e.ConstantCollection.Values)
{
foreach (Constant c2 in e.ConstantCollection.Values)
{
if (c.Name != c2.Name && c.Value == c2.Value)
{
int prefer = OrderOfPreference(Utilities.GetGL2Extension(c.Name), Utilities.GetGL2Extension(c2.Name));
if (prefer == -1)
removed_tokens.Add(c2);
else if (prefer == 1)
removed_tokens.Add(c);
}
}
}
foreach (Constant c in removed_tokens)
e.ConstantCollection.Remove(c.Name);
removed_tokens.Clear();
}
}
}
// Return -1 for ext1, 1 for ext2 or 0 if no preference.
int OrderOfPreference(string ext1, string ext2)
{
// If one is empty and the other not, prefer the empty one (empty == core)
// Otherwise check for Arb and Ext. To reuse the logic for the
// empty check, let's try to remove first Arb, then Ext from the strings.
int ret = PreferEmpty(ext1, ext2);
if (ret != 0)
return ret;
ext1 = ext1.Replace("Arb", ""); ext2 = ext2.Replace("Arb", "");
ret = PreferEmpty(ext1, ext2);
if (ret != 0)
return ret;
ext1 = ext1.Replace("Ext", ""); ext2 = ext2.Replace("Ext", "");
return PreferEmpty(ext1, ext2);
}
// Prefer the empty string over the non-empty.
int PreferEmpty(string ext1, string ext2)
{
if (String.IsNullOrEmpty(ext1) && !String.IsNullOrEmpty(ext2))
return -1;
else if (String.IsNullOrEmpty(ext2) && !String.IsNullOrEmpty(ext1))
return 1;
else
return 0;
}
new bool TryGetValue(string key, out Enum value)
{
return base.TryGetValue(key, out value);
}
}
#endregion
}