Changeset 1776


Ignore:
Timestamp:
05/08/09 16:40:43 (13 years ago)
Author:
epitzer
Message:

Type name parser that recursively strips version information (#613)

Location:
trunk/sources/HeuristicLab.Persistence
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/sources/HeuristicLab.Persistence/3.3/Auxiliary/TypeStringBuilder.cs

    r1703 r1776  
    11using System;
    22using System.Text;
     3using System.Text.RegularExpressions;
     4using System.Reflection.Emit;
     5using System.Collections.Generic;
    36
    47namespace HeuristicLab.Persistence.Auxiliary {
    58
    6   internal static class TypeStringBuilder {
     9  public class ParseError : Exception {
     10    public ParseError(string message) : base(message) { }
     11  }
     12
     13  public class TypeNameParser {
     14
     15    /*
     16      TypeSpec := SimpleTypeSpec '&'?
     17
     18      SimpleTypeSpec := (IDENTIFIER '.')*
     19                        (IDENTIFIER '+')*
     20                         IDENTIFIER
     21                        ( '`\d+[' Generics ']' )?
     22                        (\*|\[(\d+\.\.\d+|\d+\.\.\.|(|\*)(,(|\*))*)\])*
     23                        (',\s*' IDENTIFIER (',\s*' AssemblyProperty)* )?
     24
     25      Generics := '[' SimpleTypeSpec ']' (',[' SimpleTypeSpec ']')
     26
     27      AssemblyProperty := 'Version=' Version
     28                 |  'PublicKey(Token)?=[a-fA-F0-9]+'
     29                 |  'Culture=[a-zA-F0-9]+'
     30
     31      Version := '\d+\.\d+\.\d+\.\d+)'
     32
     33      IDENTIFIER = [a-zA-Z][a-ZA-Z0-9]*
     34    */
     35
     36
     37    class Token {
     38      private static Dictionary<string, string> tokens =
     39        new Dictionary<string, string> {
     40          {"&", "AMPERSAND"},
     41          {".", "DOT"},
     42          {"+", "PLUS"},
     43          {",", "COMMA"},
     44          {"[", "OPEN_BRACKET"},
     45          {"]", "CLOSE_BRACKET"},
     46          {"*", "ASTERISK"},
     47          {" ", "SPACE"},
     48          {"=", "EQUALS"},
     49          {"`", "BACKTICK"} };
     50      private static Regex NumberRegex = new Regex("^\\d+$");
     51      private static Regex TokenRegex = new Regex("[&.+,\\[\\]* =`]|\\d+|[a-zA-Z][a-zA-Z0-9]*");
     52      public string Name { get; private set; }
     53      public string Value { get; private set; }
     54      public int? Number { get; private set; }
     55      private Token(string value) {
     56        if (tokens.ContainsKey(value)) {
     57          Name = tokens[value];
     58        } else if (NumberRegex.IsMatch(value)) {
     59          Number = int.Parse(value);
     60        } else {
     61          Value = value;
     62        }
     63      }
     64      public static IEnumerable<Token> Tokenize(string s) {
     65        foreach (Match m in TokenRegex.Matches(s)) {
     66          yield return new Token(m.Value);
     67        }
     68      }
     69      public override string ToString() {
     70        if (Name != null)
     71          return "Token(" + Name + ")";
     72        if (Value != null)
     73          return "\"" + Value + "\"";
     74        if (Number != null)
     75          return Number.ToString();
     76        return "<empty>";
     77      }
     78    }
     79
     80    Queue<Token> tokens;
     81
     82    private TypeNameParser(string s) {
     83      tokens = new Queue<Token>(Token.Tokenize(s));
     84    }
     85
     86    public static string StripVersion(string s) {
     87      TypeNameParser p = new TypeNameParser(s);
     88      return p.TransformTypeSpec();
     89    }
     90
     91
     92    private string TransformTypeSpec() {
     93      string result = TransformSimpleTypeSpec();
     94      if (ConsumeToken("AMPERSAND")) {
     95        return result + "&";
     96      } else {
     97        return result;
     98      }
     99    }
     100
     101    private string TransformSimpleTypeSpec() {
     102      List<string> nameSpace = new List<string>();
     103      nameSpace.Add(ConsumeIdentifier());
     104      while (ConsumeToken("DOT"))
     105        nameSpace.Add(ConsumeIdentifier());
     106      List<string> typeName = new List<string>();
     107      if (nameSpace.Count > 0) {
     108        typeName.Add(nameSpace[nameSpace.Count - 1]);
     109        nameSpace.RemoveAt(nameSpace.Count - 1);
     110      }
     111      while (ConsumeToken("PLUS"))
     112        typeName.Add(ConsumeIdentifier());
     113      string genericString = "";
     114      if (ConsumeToken("BACKTICK")) {
     115        string number = ConsumeNumber().ToString();
     116        ConsumeToken("OPEN_BRACKET", true);
     117        string generics = TransformGenerics();
     118        ConsumeToken("CLOSE_BRACKET", true);
     119        genericString = "`" + number + "[" + generics + "]";
     120      }
     121      StringBuilder pointerOrArray = new StringBuilder();
     122      while (true) {
     123        if (ConsumeToken("ASTERSIK")) {
     124          pointerOrArray.Append("*");
     125        } else if (ConsumeToken("OPEN_BRACKET")) {
     126          ParseDimension(pointerOrArray);
     127          while (ConsumeToken("COMMA")) {
     128            pointerOrArray.Append(",");
     129            ParseDimension(pointerOrArray);
     130          }
     131          ConsumeToken("CLOSE_BRACKET", true);
     132        } else {
     133          break;
     134        }
     135      }
     136      string assembly = "";
     137      if (ConsumeComma()) {
     138        assembly = ConsumeIdentifier();
     139        while (ConsumeComma()) {
     140          TransformAssemblyProperty();
     141        }
     142      }
     143      return string.Join(".", nameSpace.ToArray()) + "." +
     144        string.Join("+", typeName.ToArray()) +
     145        genericString +
     146        pointerOrArray.ToString() +
     147        ", " +
     148        assembly;
     149    }
     150
     151    private void ParseDimension(StringBuilder sb) {
     152      if (ConsumeToken("ASTERSIK")) {
     153        sb.Append("*");
     154      } else if (CanConsumeNumber()) {
     155        sb.Append(ConsumeNumber());
     156        ConsumeToken("DOT", true);
     157        ConsumeToken("DOT", true);
     158        if (ConsumeToken("DOT")) {
     159          sb.Append("...");
     160        } else {
     161          sb.Append("..").Append(ConsumeNumber());
     162        }
     163      }
     164    }
     165
     166    private string TransformGenerics() {
     167      ConsumeToken("OPEN_BRACKET", true);
     168      List<string> typenames = new List<string>();
     169      typenames.Add(TransformSimpleTypeSpec());
     170      ConsumeToken("CLOSE_BRACKET", true);
     171      while (ConsumeToken("COMMA")) {
     172        ConsumeToken("OPEN_BRACKET", true);
     173        typenames.Add(TransformSimpleTypeSpec());
     174        ConsumeToken("CLOSE_BRACKET", true);
     175      }
     176      return "[" + string.Join("],[", typenames.ToArray()) + "]";
     177    }
     178
     179    private string TransformAssemblyProperty() {
     180      if (ConsumeIdentifier("Version")) {
     181        ConsumeToken("EQUALS", true);
     182        TransformVersion();
     183      } else if (ConsumeIdentifier("PublicKey")) {
     184        ConsumeToken("EQUALS", true);
     185        ConsumeIdentifier();
     186      } else if (ConsumeIdentifier("PublicKeyToken")) {
     187        ConsumeToken("EQUALS", true);
     188        ConsumeIdentifier();
     189      } else if (ConsumeIdentifier("Culture")) {
     190        ConsumeToken("EQUALS", true);
     191        ConsumeIdentifier();
     192      } else if (ConsumeIdentifier("Custom")) {
     193        ConsumeToken("EQUALS", true);
     194        ConsumeIdentifier();
     195      } else {
     196        throw new ParseError(String.Format(
     197          "Invalid assembly property \"{0}\"",
     198          tokens.Peek().ToString()));
     199      }
     200      return "";
     201    }
     202    private string TransformVersion() {
     203      ConsumeNumber();
     204      ConsumeToken("DOT");
     205      ConsumeNumber();
     206      ConsumeToken("DOT");
     207      ConsumeNumber();
     208      ConsumeToken("DOT");
     209      ConsumeNumber();
     210      return "";
     211    }
     212
     213    private bool CanConsumeNumber() {
     214      if (tokens.Count == 0)
     215        return false;
     216      return tokens.Peek().Number != null;
     217    }
     218
     219    private int ConsumeNumber() {
     220      if (tokens.Count == 0)
     221        throw new ParseError("End of input while expecting number");
     222      if (tokens.Peek().Number != null)
     223        return (int)tokens.Dequeue().Number;
     224      throw new ParseError(string.Format(
     225        "Number expected, found \"{0}\" instead",
     226        tokens.Peek().ToString()));
     227    }
     228
     229    private bool ConsumeIdentifier(string value) {
     230      if (tokens.Count == 0)
     231        return false;
     232      if (tokens.Peek().Value == value) {
     233        tokens.Dequeue();
     234        return true;
     235      } else {
     236        return false;
     237      }
     238    }
     239
     240    private string ConsumeIdentifier() {
     241      if (tokens.Count == 0)
     242        throw new ParseError("End of input while expecting identifier");
     243      if (tokens.Peek().Value != null)
     244        return tokens.Dequeue().Value;
     245      throw new ParseError(String.Format(
     246        "Identifier expected, found \"{0}\" instead",
     247        tokens.Peek().ToString()));
     248    }
     249
     250    private bool ConsumeComma() {
     251      if (ConsumeToken("COMMA")) {
     252        while (ConsumeToken("SPACE")) ;
     253        return true;
     254      } else {
     255        return false;
     256      }
     257    }
     258
     259    private bool ConsumeToken(string name) {
     260      return ConsumeToken(name, false);
     261    }
     262
     263    private bool ConsumeToken(string name, bool force) {
     264      if (tokens.Count == 0)
     265        if (force)
     266          throw new ParseError(String.Format(
     267            "end of input while expecting token \"{0}\"",
     268            name));
     269        else
     270          return false;
     271      if (tokens.Peek().Name == name) {
     272        tokens.Dequeue();
     273        return true;
     274      } else if (force) {
     275        throw new ParseError(String.Format(
     276          "expected \"{0}\" found \"{1}\"",
     277          name,
     278          tokens.Peek().ToString()));
     279      } else {
     280        return false;
     281      }
     282    }
     283
     284  }
     285
     286  public class TypeName {
     287    public bool IsReference { get; private set; }
     288    public bool IsArray { get { return Dimension.Length > 0; } }
     289    public bool IsPointer { get; private set; }
     290    public bool IsGeneric { get { return GenericArgs.Count > 0; } }
     291    public List<TypeName> GenericArgs { get; private set; }
     292    public string Dimension { get; private set; }
     293    public string ClassName { get; private set; }
     294    public string Namespace { get; private set; }
     295    public string AssemblyNmae { get; private set; }
     296    public string AssemblyAttribues { get; private set; }
     297    public TypeName(string typeName) {
     298    }
     299  }
     300
     301  public static class TypeStringBuilder {
    7302
    8303    internal static void BuildDeclaringTypeChain(Type type, StringBuilder sb) {
     
    32327    }
    33328
     329    public static string StripVersion(string typename) {
     330      return TypeNameParser.StripVersion(typename);
     331    }
     332
    34333  }
    35334
  • trunk/sources/HeuristicLab.Persistence/UnitTests/UseCases.cs

    r1734 r1776  
    1414using HeuristicLab.Persistence.Default.Xml.Primitive;
    1515using HeuristicLab.Persistence.Default.Decomposers;
     16using HeuristicLab.Persistence.Auxiliary;
    1617
    1718namespace HeuristicLab.Persistence.UnitTest {
     
    522523    }
    523524
     525    [TestMethod]
     526    public void TestTypeStringConversion() {
     527      string version = TypeStringBuilder.StripVersion(typeof(List<int>[]).AssemblyQualifiedName);
     528      Assert.AreEqual(version, typeof(List<int>[]).VersionInvariantName());
     529    }
     530
    524531    [ClassInitialize]
    525532    public static void Initialize(TestContext testContext) {
    526533      ConfigurationService.Instance.Reset();
    527     }
    528   }
     534    }   
     535  } 
    529536}
Note: See TracChangeset for help on using the changeset viewer.