9.4.1 环境
环境的定义具有如下不变式和操作规约。
[1] 属性EMPTYP_ENV用作一个助手以避免 new Environment(...)
context Environment
inv EMPTY_ENV_Definition: EMPTY_ENV.namedElements->isEmpty()
[2] 基于名称来当前环境(而非在它的父亲)中查找一个命名元素。
context Environment::lookupLocal(name : String) : NamedElement
post: result = namedElements->any(v | v.name = name)
[3] 基于名称在当前的环境或它的福环境中递归的查找一个命名元素。
context Environment::lookup(name: String) : ModelElement
post: result = if not lookupLocal(name).oclIsUndefined() then
lookupLocal(name).referredElement
else
parent.lookup(name)
endif
[4] 基于路径名在当前的环境或它的父环境中递归的查找一个命名元素。
context Environment::lookupPathName(names: Sequence(String)) : ModelElement
post: let firstNamespace : ModelElement = lookupLocal( names->first() ).referredElement
in
if firstNamespace.oclIsKindOf(Namespace)
-- indicates a sub namespace of the namespace in which self is present
then
result = self.nestedEnvironment().addNamespace(
firstNamespace ).lookupPathName( names->subSequence(2, names->size()) )
else
-- search in surrounding namespace
result = parent.lookupPathName( names )
endif
[5] 向环境中添加新的命名元素。注意该操作定义为一个只读操作,因此可以用于OCL约束。
context Environment::addElement (name : String,
elem : ModelElement, imp : Boolean) : Environment
pre : -- the name must not clash with names already existing in this environment
self.lookupLocal(name).oclIsUndefined()
post: result.parent = self.parent and
result.namedElements->includesAll (self.namedElements) and
result.namedElements->count (v | v.oclIsNew()) = 1 and
result.namedElements->forAll (v | v.oclIsNew() implies
v.name = name and v.referredElement = elem) and
v.mayBeImplicit = imp )
[6] 合并两个环境产生一个新环境。注意该操作被定义为一个只读操作,从而可以用于OCL约束。
context Environment::addEnvironment(env : Environment) : Environment
pre : -- the names must not clash with names already existing in this environment
enf.namedElements->forAll(nm | self.lookupLocal(nm).oclIsUndefined() )
post: result.parent = self.parent and
result.namedElements = self.namedElements->union(env.namedElements)
[7] 把命名空间中的所有元素添加到环境中。
context Environment::addNamespace(ns: Namespace) : Environment
post: result.namedElements = ns.getEnvironmentWithoutParents().namedElements->union(
self.namedElements)
post: result.parent = self.parent
[8] 在当前环境内产生一个新的环境。
context Environment::nestedEnvironment() : Environment
post: result.namedElements->isEmpty()
post: result.parent = self
post: result.oclIsNew()
[9] 在当前环境(包括它的父亲)内查找一个隐式命名元素的给定属性名)。
context Environment::lookupImplicitAttribute(name: String) : Attribute
pre: -- none
post: result =
lookupImplicitSourceForAttribute(name).referredElement.oclAsType(Attribute)
[10] 在当前的环境(包括它的父亲)内查找一个给定属性名的隐式source。
context Environment::lookupImplicitSourceForAttribute(name: String) : NamedElement
pre: -- none
post: let foundElement : NamedElement =
namedElements->select(mayBeImplicit)
->any( ne | not ne.getType().lookupAttribute(name).oclIsUndefined() ) in
result = if foundElement.oclIsUndefined() then
self.parent.lookupImplicitSource ForAttribute(name)
else
foundElement
endif
[11] 在当前的环境(包括它的父亲)内查找一个隐式命名元素的给定关联端。
context Environment::lookupImplicitAssociationEnd(name: String) : AssociationEnd
pre: -- none
post: let foundAssociationEnd : AssociationEnd =
namedElements->select(mayBeImplicit)
->any( ne | not ne.getType().lookupAssociationEnd(name).oclIsUndefined() ) in
result = if foundAssociationEnd.oclIsUndefined() then
self.parent.lookupImplicitAssociationEnd(name)
else
foundAssociationEnd
endif
[12] 在当前的环境(包括它的父亲)内利用给定的名称和参数类型查找一个隐式命名元素的操作。
context Environment::lookupImplicitOperation(name: String,
params : Sequence(Classifier)) : Operation
pre: -- none
post: let foundOperation : Operation = namedElements->select(mayBeImplicit)
->any( ne | not ne.getType().lookupOperation(name, params).oclIsUndefined() ) in
result = if foundOperation.oclIsUndefined() then
self.parent.lookupImplicitOperation(name)
else
foundOperation
endif
在OCL 2.0和2.2中,关键字可以加下划线前缀来用作一个名称。因此,为了兼容性,对带有下划线前缀的simpleNameCS[A]名称的查找需要执行两次。第一次查找带下划线前缀的,如果没有找到,再查找不带下划线前缀的。
对于simpleNameCS[B]或[C](有单引号包裹)的名称不需要进行二次查找。
删除下划线前缀的第二次查找在OCL 2.3中已经不推荐使用,并将在OCL 3.0中不再支持。对于此不推荐的使用,工具实现者应用提供一个告警消息。