388 lines
13 KiB
C#
388 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.IO;
|
|
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;
|
|
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()
|
|
{ }
|
|
|
|
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 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
|
|
|
|
List<Constant> removed_tokens = new List<Constant>();
|
|
|
|
foreach (Enum e in 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
|
|
}
|