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类树结构源码

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值