Opentk/Source/Bind/Structures/Enum.cs

398 lines
13 KiB
C#
Raw Normal View History

#region --- License ---
/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos
* See license.txt for license info
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
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, _type;
static bool enumsLoaded;
#region Initialize
internal static void Initialize(string enumFile, string enumextFile, string auxFile)
{
Initialize(enumFile, enumextFile);
if (!String.IsNullOrEmpty(auxFile))
using (StreamReader sr = new StreamReader(Path.Combine(Settings.InputPath, auxFile)))
{
AuxEnums = 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 = MainClass.Generator.ReadEnums(sr);
}
}
if (!String.IsNullOrEmpty(enumextFile))
{
using (StreamReader sr = Utilities.OpenSpecFile(Settings.InputPath, enumextFile))
{
foreach (Enum e in MainClass.Generator.ReadEnums(sr).Values)
{
//enums.Add(e.Name, e);
Utilities.Merge(GLEnums, e);
}
}
}
enumsLoaded = true;
}
}
#endregion
#region Constructors
public Enum()
{
}
#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; }
}
// Typically 'long' or 'int'. Default is 'int'.
public string Type
{
get { return String.IsNullOrEmpty(_type) ? "int" : _type; }
set { _type = value; }
}
#endregion
#region ConstantCollection
2009-02-28 18:50:16 +00:00
Dictionary<string, Constant> _constant_collection = new Dictionary<string, Constant>();
2009-02-28 18:50:16 +00:00
public IDictionary<string, Constant> ConstantCollection
{
get { return _constant_collection; }
}
#endregion
#region TranslateName
// Translate the constant's name to match .Net naming conventions
public static string TranslateName(string name)
{
2009-06-30 08:39:35 +00:00
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.
if ((Settings.Compatibility & Settings.Legacy.NoAdvancedEnumProcessing) == Settings.Legacy.None)
{
// Detect if we just passed a '_' or a number and make the next char uppercase.
bool is_after_underscore_or_number = true;
// Detect if previous character was uppercase, and turn the current one to lowercase.
bool is_previous_uppercase = false;
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;
translator.Append(char_to_add);
is_previous_uppercase = Char.IsUpper(c);
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();
2009-02-28 18:50:16 +00:00
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.Append("public enum ");
sb.Append(Name);
sb.Append(" : ");
sb.AppendLine(Type);
sb.AppendLine("{");
2009-02-28 18:50:16 +00:00
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/replace/enum[@name='{0}']";
// Translate enum names.
{
List<string> keys_to_update = new List<string>();
foreach (Enum e in 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];
Remove(name);
Add(e.Name, e);
}
keys_to_update = null;
}
foreach (Enum e in 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 Values)
{
// Skip generic GLenum
if (@enum.Name == "GLenum")
continue;
if (@enum.ConstantCollection.ContainsKey(c.Value))
{
c.Reference = @enum.Name;
break;
}
}
}
}
}
foreach (Enum e in 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
2009-02-28 18:50:16 +00:00
List<Constant> removed_tokens = new List<Constant>();
foreach (Enum e in Values)
{
if (e.Name == "All")
continue;
2009-02-28 18:50:16 +00:00
// 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)
2009-02-28 18:50:16 +00:00
removed_tokens.Add(c2);
else if (prefer == 1)
2009-02-28 18:50:16 +00:00
removed_tokens.Add(c);
}
}
}
2009-02-28 18:50:16 +00:00
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)
{
2009-02-28 18:50:16 +00:00
// 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.
2009-02-28 18:50:16 +00:00
int ret = PreferEmpty(ext1, ext2);
if (ret != 0)
return ret;
ext1 = ext1.Replace("Arb", ""); ext2 = ext2.Replace("Arb", "");
2009-02-28 18:50:16 +00:00
ret = PreferEmpty(ext1, ext2);
if (ret != 0)
return ret;
ext1 = ext1.Replace("Ext", ""); ext2 = ext2.Replace("Ext", "");
2009-02-28 18:50:16 +00:00
return PreferEmpty(ext1, ext2);
}
// Prefer the empty string over the non-empty.
2009-02-28 18:50:16 +00:00
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
}