zoukankan      html  css  js  c++  java
  • IronRuby实现对Ruby类相关信息的树结构显示(C#)

    一、前言

    IronRuby是.NET下的一个Ruby实现,此外还有Ruby.net这一开源项目,二者的主要区别是IronRuby利用了Microsoft最新推出的DLR,而Ruby.net则是完全利用原有的CLR实现的。IronRuby入门可参阅http://msdn.microsoft.com/zh-cn/magazine/dd434651.aspx。关于IronRuby的一些基本操作,本文不会涉及,本文仅仅是IronRuby对Ruby操作的一个具体实例。其中包括对所有Ruby类的类名,方法名以及参数列表的获取与显示相关的树结构。究其原因采用IronRuby来进行操作,主要是因为通过Ruby的反射可以获取到Ruby方法名列表,但是获取不到方法的参数列表与参数名称。此文仅供参考,因为本人也对IronRuby接触不是很久,基本上是摸索出来的,难免会有错误的地方。

    二、类图设计

    相关类图设计如下,其中RubyScriptEngine主要负责通过IronRuby来获取和构造相关的类名、方法名与参数列表以及之间的相关关系。TreeDrawer主要负责设计类名、方法名与参数列表相对应的树形结构图。

    三、详细设计

    (1)RubyScriptEngine主要负责通过IronRuby来获取和构造相关的类名、方法名与参数列表以及之间的相关关系。RubyScriptEngine将Ruby文件进行加载,然后动态获取文件中包含的类、方法与方法参数列表。

    具体代码如下:

      1     public static class RubyScriptEngine
      2     {
      3         private static readonly ScriptEngine engine = null;
      4 
      5         static RubyScriptEngine()
      6         {
      7             engine = Ruby.CreateEngine();
      8         }
      9 
     10         public static bool InitRelativeFiles(string directory)
     11         {
     12             if (!Directory.Exists(directory))
     13             {
     14                 return false;
     15             }
     16             
     17             string[] files = Directory.GetFiles(directory);
     18             for (int index = 0; index < files.Length; index++)
     19             {
     20                 InitRelativeFile(files[index]);
     21             }
     22 
     23             return true;
     24         }
     25 
     26         public static bool InitRelativeFile(string fileName) 
     27         {
     28             if (!File.Exists(fileName))
     29             {
     30                 return false;
     31             }
     32 
     33             try
     34             {
     35                 FileInfo fileInfo = new FileInfo(fileName);
     36                 if (string.Equals(fileInfo.Extension, ".rb", StringComparison.CurrentCultureIgnoreCase))
     37                 {
     38                     engine.ExecuteFile(fileName);
     39                 }
     40             }
     41             catch
     42             {
     43                 return false;
     44             }
     45            
     46             return true;
     47         }
     48 
     49         public static IList<string> GetClassNames()
     50         {
     51            return  engine.Runtime.Globals.GetVariableNames().ToList();
     52         }
     53 
     54         public static IList<ClassItem> GetClassesInfos()
     55         {
     56             IList<string> names = GetClassNames();
     57             IList<ClassItem> items = new List<ClassItem>();
     58             foreach (string name in names)
     59             {
     60                 items.Add(GetClassInfo(name));
     61             }
     62 
     63             return items;
     64         }
     65 
     66         public static ClassItem GetClassInfo(string className, params object[] parameters) 
     67         {
     68             RubyClass rubyClass = engine.Runtime.Globals.GetVariable(className);
     69             dynamic instance = engine.Operations.CreateInstance(rubyClass, parameters);
     70             ClassItem classItem = new ClassItem(className);
     71 
     72             IList<string> memberNames = engine.Operations.GetMemberNames(instance);
     73 
     74             MethodItem methodItem = null;
     75             ParameterItem parameterItem = null;  
     76             foreach (string memberName in memberNames)
     77             {
     78                 RubyMethodInfo methodInfo = rubyClass.GetMethod(memberName) as RubyMethodInfo;
     79 
     80                 if (methodInfo == null)
     81                 {
     82                     continue;
     83                 }
     84 
     85                 methodItem = new MethodItem(memberName,className);
     86 
     87                 RubyArray parameterArray = methodInfo.GetRubyParameterArray();
     88                 SimpleAssignmentExpression[] expressions = methodInfo.Parameters.Optional;  
     89 
     90                 for (int index = 0; index < parameterArray.Count; index++)
     91                 {           
     92                     RubyArray vas = parameterArray[index] as RubyArray;
     93                     string type = vas[0].ToString();
     94                     string name = vas[1].ToString();
     95                     parameterItem = new ParameterItem(name);
     96                     if (type == "rest")
     97                     {
     98                         parameterItem.DefaultName = "*" + name;
     99                         parameterItem.Description = RubyResource.ArrayParamDesc;
    100                     }
    101                     else if (type == "opt")
    102                     {
    103                         for (int eindex = 0; eindex < expressions.Length; eindex++)
    104                         {
    105                             SimpleAssignmentExpression ex = expressions[eindex];
    106                             Variable variable = ex.Left as Variable;
    107                             if (!string.Equals(variable.Name, name))
    108                             {
    109                                 continue;
    110                             }
    111 
    112                             Literal literal = ex.Right as Literal;           
    113                             parameterItem.DefaultName = name;
    114                             parameterItem.DefaultValue = literal.Value;
    115                             parameterItem.Description = RubyResource.DefaultParamDesc;
    116                         }
    117                     }
    118                     else if (type == "block")
    119                     {
    120                         parameterItem.DefaultName = "&" + name;
    121                         parameterItem.Description = RubyResource.BlockParamDesc;
    122                     }
    123                     else
    124                     {
    125                         parameterItem.DefaultName = name;
    126                     }
    127                     methodItem.Parameters.Add(parameterItem);        
    128                 }
    129                 classItem.Methods.Add(methodItem);
    130             }
    131 
    132             return classItem;
    133         }
    134     }

    其中相关方法如下:

    public static bool InitRelativeFiles(string directory)  根据目录加载目录下的Ruby文件

    public static bool InitRelativeFile(string fileName)    根据文件名加载该Ruby文件

    public static IList<string> GetClassNames()            获取所有的类名

    public static IList<ClassItem> GetClassesInfos()     获取所有类的信息

    public static ClassItem GetClassInfo(string className, params object[] parameters)    根据类名和类的构造函数参数获取对应的类信息

    类的信息显示效果如下(左侧显示类的信息,右侧编辑器显示类的基本结构):

    (2)TreeDrawer主要用于绘制类的树结构,根据不同的类结构显示不同的效果。这里是用Winform来显示的,本来打算用Silverlight来实现,但是由于时间关系,将就着这样算了。当然,Silverlight显示的效果比Winform强多了,而且,本人Silverlight水平比Winform熟练很多(以前项目中用Silverlight动态绘制相关图形,因此比较熟悉)....

    TreeDrawer的主要方法为以下2个:

     1         public Bitmap CreateImage(ClassItem classItem, Font font)
     2         {
     3             if (classItem == null || font == null)
     4             {
     5                 return null;
     6             }
     7 
     8             ClassBlock classBlock = CreateCurrentClassBlock(classItem);
     9             AddLinesAndBlockTexts(classBlock, font);
    10 
    11             Bitmap bitmap = new Bitmap(3 * BLOCK_WIDTH + 2 * BLOCK_INNER_WIDTH, this.Height);
    12             Graphics graphics = Graphics.FromImage(bitmap);
    13             graphics.Clear(Color.White);
    14             Pen ellipsePen = new Pen(Color.Blue, 2);
    15 
    16             foreach (Line line in this.Lines)
    17             {
    18                 graphics.DrawLine(ellipsePen, line.X1, line.Y1, line.X2, line.Y2);
    19                 if (line.HasArrow)
    20                 {
    21                     PointF[] points = CreateArrowPoints(new PointF(line.X1, line.Y1),
    22                         new PointF(line.X2, line.Y2), ARROW_LENGTH, RELATIVE_VALUE);
    23                     DrawArrowHead(graphics, points);
    24                 }
    25             }
    26 
    27             foreach (BlockText content in this.Contents)
    28             {
    29                 graphics.DrawString(content.Content, font, Brushes.Black, content.StartX, content.StartY);
    30             }
    31 
    32             return bitmap;
    33         }
    34 
    35         private ClassBlock CreateCurrentClassBlock(ClassItem classItem)
    36         {
    37             int originalParamY = 0;
    38             int lastParamY = 0;
    39             int currentParamY = 0;
    40             int currentMethodY = 0;
    41             int startMethodX = BLOCK_WIDTH + BLOCK_INNER_WIDTH;         
    42             int startParamX = 2 * BLOCK_WIDTH + 2 * BLOCK_INNER_WIDTH;
    43 
    44             List<MethodBlock> methodBlocks = new List<MethodBlock>();
    45             foreach (MethodItem methodItem in classItem.Methods)
    46             {
    47                 int paramsCount = methodItem.Parameters.Count;
    48                 if (paramsCount > 0)
    49                 {
    50                     lastParamY += paramsCount * (BLOCK_HEIGHT + BLOCK_INNER_HEGIHT);
    51                 }
    52                 else
    53                 {
    54                     lastParamY += (BLOCK_HEIGHT + BLOCK_INNER_HEGIHT);
    55                     currentParamY += (BLOCK_HEIGHT + BLOCK_INNER_HEGIHT);
    56                 }
    57 
    58                 currentMethodY = ((lastParamY - BLOCK_INNER_HEGIHT - originalParamY) / 2 + originalParamY - (BLOCK_HEIGHT / 2));
    59                 originalParamY = lastParamY;
    60 
    61                 MethodBlock methodBlock = new MethodBlock(new Point(startMethodX, currentMethodY), BLOCK_WIDTH, BLOCK_HEIGHT);
    62                 methodBlock.Content = methodItem.Name;
    63                 methodBlocks.Add(methodBlock);
    64                 foreach (ParameterItem parameterItem in methodItem.Parameters)
    65                 {
    66                     ParameterBlock parameterBlock = new ParameterBlock(new Point(startParamX, currentParamY), BLOCK_WIDTH, BLOCK_HEIGHT);
    67                     parameterBlock.Content = parameterItem.DefaultName;
    68                     methodBlock.ParameterBlocks.Add(parameterBlock);
    69                     currentParamY += (BLOCK_HEIGHT + BLOCK_INNER_HEGIHT);
    70                 }
    71             }
    72 
    73             if (lastParamY > 0)
    74             {
    75                 lastParamY -= BLOCK_INNER_HEGIHT;
    76             }
    77             else
    78             {
    79                 lastParamY = BLOCK_HEIGHT;
    80             }
    81 
    82             this.Height = lastParamY;
    83 
    84             Point classStartPoint = new Point(00);
    85             if (classItem.Methods.Count > 1)
    86             {
    87                 int y = (methodBlocks.Last().LeftBottomPoint.Y - methodBlocks.First().LeftBottomPoint.Y) / 2;
    88                 classStartPoint = new Point(0, y);
    89             }
    90 
    91             ClassBlock classBlock = new ClassBlock(classStartPoint, BLOCK_WIDTH, BLOCK_HEIGHT);
    92             classBlock.Content = classItem.Name;
    93             foreach (MethodBlock methodBlock in methodBlocks)
    94             {
    95                 classBlock.MethodBlocks.Add(methodBlock);
    96             }
    97 
    98             return classBlock;
    99         }

    public Bitmap CreateImage(ClassItem classItem, Font font)             主要负责绘制Bitmap图片

    private ClassBlock CreateCurrentClassBlock(ClassItem classItem)    主要负责将ClassItem(类信息形式)转换成ClassBlock (坐标形式),并负责计算相应的坐标

    类的树结构显示效果如下:

    当然,还可以定义其他格式的类,显示的效果根据类的不同绘制相应的树结构。其中,TreeDrawer中比较简单的算法会自动设置合理的坐标,以生成相应的树结构坐标。本处一切以简单进行处理,不然的话,参数设置是比较多的。

    四、总结

    IronRuby是.NET下的一个Ruby实现 ,对于实现单个类的操作来说,用.NET 4.0中的Dynamic更加方便与美观,如调用PersonClass类的nonArgsMethod方法即可写成如下格式:

    dynamic globals= engine.Runtime.Globals;   
    dynamic apple = globals.PersonClass.@new();  //构造实例
    apple.nonArgsMethod();    //调用方法

    本文中采用如下代码进行调用,主要是为了通用性处理。通过类名称以及构造函数的参数来动态获取类的信息。

    RubyClass rubyClass = engine.Runtime.Globals.GetVariable(className);
    dynamic instance = engine.Operations.CreateInstance(rubyClass, parameters);
    ClassItem classItem = new ClassItem(className);
    IList<string> memberNames = engine.Operations.GetMemberNames(instance);

    源代码下载地址:IronRuby Ruby类树结构源码

  • 相关阅读:
    批处理手动设置电脑的ip
    用shell脚本生成日志文件
    编译小米mini openwrt
    Jackson 时间格式化,时间注解 @JsonFormat 用法、时差问题说明
    swagger注释API详细说明
    com.rabbitmq.client.impl.Frame.readFrom(Frame.java:95)
    在Java中如何高效判断数组中是否包含某个元素
    阿里云服务器端口开放对外访问权限
    macos 安装telnet命令
    在 Docker 上配置 Oracle
  • 原文地址:https://www.cnblogs.com/jasenkin/p/ironruby_ruby_class_tree.html
Copyright © 2011-2022 走看看