EMF学习笔记6——模型验证
EMF模型的验证主要通过ModeValidationService类的调用来完成
由类图可以看到,ModelValidationService其实是一个单例模式,声明了getInstance()方法用于获取类的实例;
同时,该类还声明了一个工厂方法newValidator()用于获取IValidator的实现类。
IValidator的子接口有两个,分别是ILiveValidator和IBatchValidator,对应模型的验证方式是Live模式还是Batch模式;而模式的区分通过EvaluationMode来指定。
注:EMF Validation Framework提供了两种模型验证方式,Batch和Live
Batch模式:静态的模型验证方式,触发方式通常通过点击某个按钮,或在固定的业务逻辑处编写验证代码;
Live模式:验证的触发通过Notification,即模型改变通知。
Batch模式的验证逻辑如下:
List objects = myResource.getContents();//验证的Input值,可以是某一个EObject或时集合
//通过ModelValidationService构建IBatchValidator
//通过EvaluationMode.BATCH指明为Batch模式
IValidator validator = ModelValidationService.getInstance().newValidator(EvaluationMode.BATCH);
validator.setIncludeLiveConstraints(true);//包含Live模式的约束
IStatus results = validator.validate(objects);//模型的验证返回IStatus对象
if (!results.isOK()) {//判断验证状态
ErrorDialog.openError(null, "Validation", "Validation Failed", results);
}
Live模式的验证逻辑和batch模式类似,只是Input值不同。
List notifications = transaction.getChanges(); //input值是Notification的集合
//构建ILiveValidator,通过EvaluationMode.LIVE指明为Live模式
ILiveValidator validator = (ILiveValidator) ModelValidationService.getInstance().newValidator(EvaluationMode.LIVE);
IStatus results = validator.validate(notifications);//对通知进行验证
if (!results.isOK()) {//判断验证结果
ErrorDialog.openError(null, "Validation", "Validation Failed", results);
}
除此之外,无论是Batch模式还是Live模式,可以为模型的验证指定过滤器来过滤掉不需要的约束和目标对象。
validate.addConstraintFilter(new IConstraintFilter() {
public boolean accept(IConstraintDescriptor constraint, EObject target) {
ConstraintSeverity sev = constraint.getSeverity();
return sev.toIStatusSeverity() >= IStatus.ERROR.
}
});
在验证之前,首先要对模型构建约束,否则验证操作便变得毫无意义,因为没有任何约束进行评估。
约束的构建主要通过继承AbstractModelConstraint抽象类来完成。
如图:AbstractModelConstraint使用了IValidationContext接口作为validate()的方法参数,并且返回Istatus对象用来封装验证结果。
而IValidationContext对象主要提供了当前验证环境的相关信息,包括:
target:所验证的目标对象;
eventType:表示Live模式下触发验证操作的事件;
currentConstraintId:当前所执行的约束Id,等等...
在validate方法中,如果约束得评估没有通过,可调用IValidationContext接口定义的createFailureStatus(Object... args)方法返回错误状态类型相反,如果评估成功,可调用createSuccessStatus()方法返回成功状态类型。
一个简单的约束对象构建如下:
public class LibraryNameIsUnique extends AbstractModelConstraint {//首先继承AbstractModelConstraint超类
public IStatus validate(IValidationContext ctx) {
Library target = (Library) ctx.getTarget(); //通过IValidationContext获取所验证的目标对象
//判断library的名称是否唯一
Set<Library> libs = findLibrariesWithName(target.getName());
if (libs.size() > 1) {//如果不唯一
ctx.addResults(libs);
libs.remove(target);
ctx.skipCurrentConstraintFor(libs);//省略其他实体类的验证
//返回错误状态类型
return ctx.createFailureStatus(new Object[] {target, libs});
}
}
//返回成功状态类型
return ctx.createSuccessStatus();
}
}
有了约束对象以后,还需要将此约束对象注入到验证环境中去,这样在执行验证操作时,Eclipse才会找到该约束进行评估。
可通过实现org.eclipse.emf.validation.constraintProviders扩展点完成约束的注入。
<extension point="org.eclipse.emf.validation.constraintProviders"> <category name="Library Constraints" id="org.eclipse.example.library"> <constraintProvider> <package namespaceUri="http:///org/eclipse/emf/examples/library/extlibrary.ecore/1.0.0"/> <constraints categories="org.eclipse.example.library"> <constraint lang="Java" class="com.example.constraints.UniqueLibraryName" //class指定约束对象 severity="WARNING" //出错级别为WARNING mode="Live" //验证模式为Live name="Library Must have a Unique Name" id="org.eclipse.example.library.LibraryNameIsUnique" statusCode="1"> <description>Libraries have unique names.</description> <message>{0} has the same name as another library.</message> //验证出错提示消息 <target class="Library"> //目标对象 <event name="Set"> //验证触发事件为set操作 <feature name="name"/> //当执行setName()时触发验证 </event> </target"> </constraint> </constraints> </constraintProvider> </extension>
通过以上的操作,在Eclipse环境中便可以找到所定义的约束,下一步要做的就是将约束与Application进行绑定。
可通过org.eclipse.emf.validation.constraintBindings扩展点来实现该功能。
<extension
point="org.eclipse.emf.validation.constraintBindings">
<clientContext
default="false"
id="org.emf.demo.validate.libraryContext">
<!--selector所声明的class实现了IClientSelector接口,其selects(Object object)方法相当于一个门闩,
来控制是否对约束进行评估-->
<selector class="org.emf.demo.validate.ValidationDelegateClientSelector"/>
</clientContext>
<binding
context="org.emf.demo.validate.libraryContext"
category="org.emf.demo.validate.library"/> //将此类型的约束绑定到所声明的clientContext中
</extension>
EMF的这种验证方式,由于使用了扩展点技术,所以只能在EclipseRCP环境下运行,而且编写扩展点的实现也非常的繁琐。
除了这种方式外,用户还可自己编写EValidator的实现,然后将该实现类注入到EValidator注册表中去,一样可完成模型验证操作,
且这种方式无需编写扩展点的实现,因此只需要OSGI环境就可以了。
首先编写EValidator的实现:
如果在*.ecore模型文件中,为模型实体指明了相关的约束,那么通过*.genmodel文件,是可以直接生成对应的EValidator实现类的。
该类会放在模型实体bundle中的util包下面,主要继承了EObjectValidator超类,且类中定义了相关的验证方法来对应模型描述文件中声明的约束。
有了该Evalidator的实现类以后,需要将其添加到EValdator注册表中去,可通过如下代码完成操作:
EValidator.Registry.INSTANCE.put
(theLibraryPackage,
new EValidator.Descriptor() {
public EValidator getEValidator() {
return new EValidatorAdapter();
}
});
注:通过*.genmodel文件为我们生成的代码中,会默认将此代码写到*PackageImpl的init方法中去。
添加到注册表中以后,我们只需编写如下代码,便可完成对模型的验证操作,省略了繁琐的扩展点编写过程:
Object target=getTartget();//target为需要验证的目标对象。
Diagnostician diagnostician=new Diagnostician();
BasicDiagnostic diagnostic=diagnostician.createDefaultDiagnostic(target);
Map<Object, Object> context = diagnostician.createDefaultContext();
diagnostician.validate(library, diagnostic, context);//执行验证操作