OCL解析器提供了两个类用于解析ocl表达式,分别是OCLHelper和OCL:
OCLHelper接口的设计直接用于解析表达式,而OCL则作为解析的切入点。
OCL的解析首先要为其创建一个环境,如:是基于UML模型的还是基于ECore模型的。
可通过在构造函数中指定相应的EnvironmentFactory来实现,如:
OCL<?, EClassifier, ?, ?, ?, ?, ?, ?, ?, Constraint, EClass, EObject> ocl;
ocl = OCL.newInstance(EcoreEnvironmentFactory.INSTANCE);//ECore环境
在OCL类中声明了两个方法getEnvironment()和getEvaluationEnvironment()分别用于获取模型的根环境和评估环境,这两个环境是由EnvironmentFactory接口创建的。
在根环境中可以嵌套一些子环境用于定义package的命名空间,以此获取package的上下文,先看一段简单的OCL描述:
--包上下文 package Company --类上下文 context Person inv: name<>'zhangsan' --操作上下文 context Person::getCurrentSpouse() : Person pre: self.isMarried = true body: self.mariages->select( m | m.ended = false ).spouse --属性上下文 context Person::income : Integer init: parents.income->sum() * 1% -- pocket allowance endpackage
在OCL描述中,是通过上下文(context)的组织来体现层级结构的,如:
由package可以遍历到classifer的上下文;
由classifer可以遍历到方法或属性的上下文;
通过这种层级结构,由package上下文可以遍历到任何模型元件。
因此,Environment接口中的各种lookup方法主要是通过package context来进行相关的查找操作。
实例化OCL对象以后,可以通过createOCLHelper()方法来获取OCLHelper对象。
OCLHelper对象声明了大量方法用于创建OCL查询、约束。
不同的约束条件需要不同的上下文环境,setContext(),setOperationContext()和setAttributeContext()分别表示了该约束是针对classifer,operation还是attribute的。
OCLHelper<EClassifier, ?, ?, Constraint> helper = ocl.createOCLHelper(); helper.setContext(EXTLibraryPackage.Literals.LIBRARY);//设置上下文环境 Constraint constraint = helper.createInvariant(//创建约束 "books->forAll(b1, b2 | b1 <> b2 implies b1.title <> b2.title)"); OCLExpression<EClassifier> query = helper.createQuery(//创建查询 "books->collect(b : Book | b.category)->asSet()");
如上:createInvariant方法会解析约束表达式,createQuery方法会解析查询表达式,两个方法分别返回Constraint对象和OCLExpression对象。
有了查询和约束对象以后,可以使用Query对象对其评估。
Query<EClassifier, EClass, EObject> queryEval = ocl.createQuery(query);//获取查询评估对象 Query<EClassifier, EClass, EObject> constraintEval = ocl.createQuery(constraint);//获取约束评估对象 List<Library> libraries = getLibraries(); for (Library next : libraries) { if (constraintEval.check(next)) {//如果约束通过 @SuppressWarnings("unchecked") Set<BookCategory> categories = (Set<BookCategory>) queryEval.evaluate(next);//执行查询评估 System.out.printf("%s: %s%n", next.getName(), categories); } }
Query API提供了实用的方法,可以简写for循环:
for (Library next : constraintEval.select(libraries)) {
@SuppressWarnings("unchecked")
Set<BookCategory> categories = (Set<BookCategory>) queryEval.evaluate(next);
System.out.printf("%s: %s%n", next.getName(), categories);
}
现实应用中,经常将模型定义和模型约束写入不同的资源文件中以便于模型的管理。
OCL提供了相应的方法来解析约束文件,通过构造OCLInput对象。
OCL<?, EClassifier, ?, ?, ?, ?, ?, ?, ?, Constraint, EClass, EObject> ocl; ocl = OCL.newInstance(EcoreEnvironmentFactory.INSTANCE); IFile file = getWorkspaceFile("/ocl/constraints.ocl");//获取ocl约束文件 InputStream in = file.getContents(); //将解析出的约束存放到HashMap中 Map<String, Constraint> constraintMap = new HashMap<String, Constraint>(); try { OCLInput document = new OCLInput(in, file.getCharset());//构造OCLInput对象 List<Constraint> constraints = ocl.parse(input);//解析ocl文档 for (Constraint next : constraints) { constraintMap.put(next.getName(), next); OCLExpression<EClassifier> body = next.getSpecification().getBodyExpression(); System.out.printf("%s: %s%n", next.getName(), body); } } finally { in.close(); }
constraints.ocl文档内容如下:
package extlibrary context Library -- get all books with a title in a library and its branches (recursively) def: getBooks(title : String) : Set(Book) = books->select(b | b.title = title)->asSet()->union( branches.getBooks(title)) context Book -- the library containing a book def: library : Library = Library.allInstances()->any(books->includes(self)) -- book titles are unique within their library branch (and its sub-branches) inv unique_title: not library.oclIsUndefined() implies library.getBooks(title) = Set{self}) endpackage
通过上面的解析,将约束条件以Map形式存储起来,可通过约束的名字得到约束对象,如:
ocl.check(book, constraintMap.get("unique_title"));//对book对象进行title唯一的约束校验。