本文主要讲述X-Meta引擎实现的主要Java类。
事物(Thing)对应的Java类是org.xmeta.Thing。事物对应的是数据模型,因此模型、事物、事物模型在这里都是同一个东西,通常我们简称事物模型为事物。
我们认为一个事物是由属性和子事物构成的。
事物的属性是用key-value(键值)的方式存储的,其中属性的名称是字符串,属性的值可以是任意Java对象。
//--------属性的相关操作----------- //设置属性,其中set和put方法作用一样 thing.set(String name, Object value); thing.put(String name, Object value); //获取属性 Object value = thing.get(String name) //获取属性:带有类型转换的方法 String value =thing.getString(String name); int value = thing.getInt(String name); int value = thing.getInt(String name, int defualtValue); double value = thing.getDouble(String name); double value = thing.getDouble(String name, double defualtValue); //其他类似的方法 xxxx value = thing.getXxx(String name); xxxx value = thing.getXxx(String name, xxxx, defualtValue);
子事物是事物的子节点,用List存储。
//--------子事物的相关操作----------- //添加子节点 thing.addChild(Thing child); thing.addChild(Thing child, int index); //获取全部子节点列表 List<Thing> childs = thing.getChilds(); //按照事物名获取对应的子节点列表 List<Thing> childs = thing.getChilds(String thingName);
事物模型认为任何事物都可以转化为动作,而动作是可以执行的程序,因此可以认为一个事物的某些子事物是它的行为。
在X-Meta引擎里我们约定一个事物的第一个事物名为actions的子事物是事物的行为定义节点,第一个事物名为actions的子事物的子事物为具体的行为。
其中事物名是什么见下面事物的描述,而事物的行为如何执行等见下面的动作。
一个事物是什么?它的结构是什么样的?它有哪些行为?我们认为可以使用描述的方法来解释,即使用一个事物去解释另一个事物,那么如果使用事物A解释事物B,那么也称事物A是事物B的描述者。
事物之间的描述关系是存放到事物的一个名为descriptors的属性里的,比如如果事物A是事物B的描述者,那么:
Thing a = new Thing(); Thing b = new Thing(); //设置事物a为事物b的描述者,其中"a"为事物a的路径 //事物的路径件下面世界(World)的文档,通过事物的路径可以访问到指定的事物 b.put("descriptors", "a"); //获取第一个描述者 Thng descritpor = b.getDescriptor(); //获取自身定义的所有描述者 List<Thing> descriptors = b.getDescriptors(); //获取所有描述者,包含描述者的继承 List<Thing> descriptors = b.getAllDescriptors();
在这里需要注意的是事物模型定义了事物之间的描述关系,但没有定义具体的解释方法,也就说你可以根据具体需要具体实现。
由于我们认为一个事物有哪些行为也是属于描述的范畴,所以如果事物A是事物B的描述者,那么我们认为事物A可以描述事物B的行为,具体实现方法就是事物B拥有事物A的行为。
因此描述者的一个重要作用就是行为的继承,这样描述者和被描述的事物有点类似于类和对象的关系,如果A是对象B的类,我们知道如果类A具有一个行为,那么对象B也应该是有的。
如果事物A是事物B的描述者,我们定义事物B的事物名为事物A。
事物名的概念取自于类和对象,如果A是对象B的类,那么我们知道可以说B就是A,比如人是张三的类,那么我们就可以说张三是人,而人是张三的事物名。
我们认为一个事物可以把任意事物当做它的描述者,甚至是它自己,个数也不限,并且一个事物的描述者是可以随时修改的。
如果一个事物的描述者有多个,那么在descriptors属性里它们的路径使用英文逗号分隔的,而行为的继承的顺序则是在descriptors属性里出现的先后顺序。
一些事物在逻辑上可能拥有无限重复的子事物节点,它可以包含子事物,子事物又可以包含子事物,子子事物又可以包含子事物,可以这样无限下去,但是它们的子节点是重复的,是一种递归的重复。像这样的事物不能在计算机中直接表示,所以我们引入了继承的概念。
我们定义如果一个事物B继承了事物A,那么在逻辑上事物B就拥有了事物A的属性和子事物。请注意这是逻辑上的拥有,并不是真的拥有,通过事物B的获取属性的方法并不会取到事物A的属性。
事物间的继承关系是用属性extends表示的,如果事物B继承事物A,那么在事物B的属性里设置事物A的路径。
Thing a = new Thing(); Thing b = new Thing(); //设置事物b继承事物a,其中"a"为事物a的路径 b.put("extends", "a"); //获取事物自身定义的继承列表 List<Thing> extendThings = b.getExtends(); //获取事物所有的继承列表,包括继承的事物的继承 List<Thing> extendThings = b.getAllExtends();
由于在逻辑上继承了事物的子事物,而一个事物的行为是由子事物定义的,因此我们认为如果事物B继承了事物A,那么事物B也拥有事物A的行为,其中行为的继承是在X-Meta引擎内部实现的。
一个事物可以继承多个事物,数量没有限制,由于继承自己没有意义,所以我们约定一个事物不能继承自己,如果一个事物继承了多个事物,那么在extends属性里用英文逗号分隔,继承行为的顺序则是按照extends里出现的顺序为优先级。
通过描述和继承可以实现面向对象几乎全部的概念,但是可以看到在事物模型里并没有类和对象的概念,这是因为使用描述和继承不仅能够实现面向对象,还可以实现其他功能,我们不局限于类和对象就是为了还可以实现其他功能,比如XWorker的事物管理器把描述者当做事物的结构,从而实现了一套编辑事物的方法。
事物模型也是可以运行的,它是通过动作(Action)实现的。
在类事物(Thing)里有一个getAction()方法用于把事物转化成动作(Action),而动作(Action)是可以执行的。
//事物到动作的转换 Action action = thing.getAction(); //动作是可以执行的,其中ActionContext是变量上下文 action.run(ActionContext actionContext)
在这里任何事物都有getAction方法,但并不是任意事物转化后的动作执行都是有意义的,动作的执行遵循一些规则。
动作对应的Java类是org.xmeta.Action。
动作执行的原理比较简单,那就是如果动作对应的事物的事物名是JavaAction,那么认为执行的是Java代码,否则就执行对应事物的run行为。
在X-Meta引擎里对应的代码片段是:
if(useOtherAction){ //如果一个动作是引用其他动作的,那么执行被引用的动作 Bindings callerBindings = context.getScope(context.getScopesSize() - 2); try{ context.push(callerBindings); result = outerAction.run(context, parameters, isSubAction); }finally{ context.pop(); } }else if(isJava){ //如果动作时原生动作,即Java动作,那么通过反射机制调用 if(actionClass != null){ if(method != null){ result = method.invoke(actionClass, new Object[]{context}); }else{ logger.info("Java action method is null, " + getThing().getMetadata().getPath()); } } }else{ //如果不是Java,那么调用当前动作事物的run行为,run行为有可能是自定义的也有可能是其描述者定义的 //此方法很重要,用这个方法可以实现其他语言的支持,比如Groovy、JavaScript等脚本语言 if(isSelfInterpretationType){ //self类型的动作,当一个动作作为子动作时,而变量self还需要是自己,那么需要定义成此类型 if(attributeTemplate){ //属性模板,动作的属性可以设置成freemarker模板,在执行时用模板生成真正的属性值 Thing fthing = (Thing) thing.run("processAttributeTemplate", context, (Map<String, Object>) null, isSubAction, true); if(fthing != null){ fthing.run("run", context, (Map<String, Object>) null, isSubAction, true); } }else{ result = thing.run("run", context, (Map<String, Object>) null, isSubAction, true); } }else{ //获取事物自己的行为run,然后执行 Thing actionThing = thing.getActionThing("run"); if(actionThing != null){ Action ac = actionThing.getAction(); result = ac.run(context, null, caller, isSubAction); } }
变量上下文对应的Java类是org.xmeta.ActionContext,变量上下文的用途是变量,包括动作执行过程中的临时变量和参数等,X-Meta引擎相当于一个虚拟机,就是靠动作和变量上下文一起实现的。
变量上下文常用的方法:
//声明变量上下文 ActionContext actionContext = new ActionContext(); //设置和读取变量 actionContext.put(String key, Object value); Object value = actionContext.get(String key); //压栈和出栈操作 Bindings bindings = actionContext.push(); actionContext.pop(); //获取相对的全局变量范围 Bindings globalScope = actionContext.getScope(0); //获取当前局部变量范围 Bindings localScope = actionContext.getScope(); //Bindings的设置和读取变量 bindings.put(String key, Object value); Object value = bindings.get(Stirng key); //获取状态 int status = actionContext.getStatus();
使用ActionContext和动作可以实现函数、方法的调用效果,即在执行一个动作时可以压入一个栈得到一个Bindings,然后在Bindings中存放参数和临时变量等,然后在执行完动作时弹出此栈。
可以通过ActionContext的status实现控制语句,status的可选值有RUNNING、RETURN、CANCEL、BREAK、CONTINUE和EXCEPTION,使用status可以用事物模型实现诸如编程语言中的return、break、continue和异常的抛出和处理等。
调用动作运行一般有两种方式,一是直接运行,另一种是被当做事物的行为来运行。
下面详细说明一下当做事物的行为来运行。
从事物的定义可知一个事物行为是它的某个子节点,或者是从其描述者和继承者继承的,其中事物的行为还是由事物定义的,因此可以通过事物获取到行为的定义事物,然后执行。
事物的行为是通过doAction方法执行的,不如:
//执行事物的名为xxx的行为 thing.doAction("xxx" , actionContext);
doAction的实现核心代码:
Thing actionThing = getActionThing(name); Action action = actionThing.getAction(); Bindings bindings = context.push(null); bindings.caller = this; bindings.put("self", this); try{ return action.run(context); }finally{ context.pop(); }
在这里可以看到在doAction里事物模型以self变量名放入到了变量上下文中,因此动作动作执行时就可知道是谁在调用它了。
与事物对应的词是世界,因此X-Meta引擎使用世界(World)来管理事物,通过World可以获取到任何一个事物。
世界对应的Java类是org.xmeta.World,World对象单实例的,World world = World.getInstance()。
为了方便管理类,事物按照ThingManager和Category分组。
事物管理器对应的Java接口是org.xmeta.ThingManager,ThingManager是一个接口,作用是存储于事物到不同的介质,比如文件系统、Jar和类路径、数据库和内存中等,每一种介质都对应各自的ThingManager的实现。
由于事物管理器是事物的第一级分组,因此事物管理器还有一个附加作用就是可以当做项目来用。
目录接口对应的Java是org.xmeta.Category,目录是事物管理器下的具体分组,比如在文件系统下目录对应的文件目录,即可以把事物放到不同的文件目录下,以便区分。
4.3路径
世界是唯一的(单实例),而事物也是唯一,每一个事物都是独一无二的,因此可以通过其路径来访问。
事物的路径是<目录> + 事物的名字,比如xworker.lang.MetaThing是在文件系统下保存的,那么xworker.lang就是相对目录xworker/lang,MetaThing + 编码类型就是文件名,比如MetaThing.xer.txt或MetaThing.xml等。
子路径规则如下:
/ :子事物的分隔符。 @xxx :寻找id为xxx的子事物 @?xxx :通过子事物的id或者name属性对比查找。 @n :n是数字,如果没有找到标识为对应数字的子事物,那么寻找第n个子事物。 xxx@ :寻找事物名为xxx的子事物列表,如果没有返回一个空的列表,不是null。 xxx@n :寻早事物名为xxx的第n的子事物,如果有标识为n的先返回。 #xxx :找名为xxx的属性。 $n :同@n,用于区别@xxx,$n只用于找第n个子事物。
World world = World.getInstance(); //获取事物 Thing thing = world.getThing("xworker.lang.MetaThing"); //直接获取子事物 Thing child = world.getThing("xworker.lang.MetaThing/@thing"); //获取属性 Object value = thing.get("#name"); //获取名为thing的第一个子事物 Thing child = thing.getThing("thing@0");
Copyright © 2007-2014 XWorker.org 版权所有