Opentk/Source/Bind/Structures/Enum.cs
the_fiddler cf4a243c99 Merged the latest specs from opengl.org.
Replaced all instances of Version14, Version15, Version20, Version30 and Version31 enums by type-safe equivalents.
Added gloverrides.xml to support overrides to function parameters / return types. This will make future spec updates easier to merge.
Improved handling of invalid tokens in the specs.
Bindings are now written in alphabetical order. This will reduce patch size for future updates and make side-by-side comparisons easier to perform.
2009-05-29 15:57:01 +00:00

347 lines
11 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;
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 (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");
return translator.ToString();
}
#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()
{
// Translate enum names.
{
List<string> keys_to_update = new List<string>();
foreach (Enum e in this.Values)
{
string name = Enum.TranslateName(e.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)
{
foreach (Constant c in e.ConstantCollection.Values)
{
// 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
}