Skip to main content

7.4 内核层 Kernel

7.4 Kernel

7.4.1 内核概览

Kernel层将KerML构建完成。 通过在基本的分类之外添加建模能力,它扩展了Core层。 包括事物的特化的分类器:具有数据值语义的元素(数据类型,data type), 在时空维度具有独立存在的事物(类,class),以及事物之间的具体的关系(关联,association)。

类的实例是在时间和空间中存在或者发生的事物,分为结构(structure)行为(behavior)。 结构通常限制了事物以及事物之间的关系如何随时间可能发生的变化, 而行为指定了在这些限制之内的变化。 结构和行为不会重叠,但是结构可以参与、执行以及拥有行为。 行为可以通过步骤(step,行为的使用)和其他的行为进行协作。 函数(function)是产生单一结果的行为,可以用来组成表达式(expression)树。 交互将行为和关联组合起来。 某些关联也是结构。

Kernel层主要通过指定模型元素如何使用Kernel模型库来对Core层添加语义, 而不是像在Core层一样通过数学的方式指定。 最简单的例子是,Kernel的文本表达式引入了一些关键字,TODO: 这些关键字将,作为建模模式的语法“标记”将Kernel和Core连接起来。 其中最简单的例子是,引入了模型库中类型的隐式特化。 例如,类必须直接或间接地对库中的类Object进行子分类, 而行为必须直接或间接地对库中的类Performance进行子分类。 某些情况下,需要更复杂的复用模式。 例如,二元关联对库中的BinaryLink进行特化,并且需要关联的端对BinaryLinksource端和target端进行重定义。

这也是其他的建模语言如何基于KerML构建的方式。 领域特定的元模型和库可以复用Kernel的元模型和库,继承上述的库复用的模式, 也包括它们从Core继承下来的数学语义。 这使得领域特定的建模者能够使用熟悉的术语和表达式,同时仍然从数学定义的语义得到的自动支持中获益。

7.4.2 数据类型

数据类型(data type)是对数据值(data value)进行分类的分类器。 特定的基本(primitive)数据类型已经制定了值的范畴,例如ScalarValues库模型中的数字和其他类型。 其他的数据类型具有特征,这些特征的值在该数据类型的不同实例中是可以区分的。 但是,不同的数据值本身是不可区分的。

这意味着数据类型不能同时是类,或者关联,也不能和它们共享实例。 这也意味着数据类型对不存在于时间或者空间中的事物进行分类,因为这些事物和其他事物之间的关系需要发生变化。 数据值的特征的值不能随时间发生变化,因为不同的不同的特征值将表示不同的数据值。

数据类型作为一个分类器,通过关键字datatype声明。 如果没有显式的给出一个父类,那么将默认把Base库模型中的数据类型DataValue作为它的父类。

如果一个特征的类型有一个是数据类型,那么该特征的所有类型都必须是数据类型。 如果一个特征的类型是数据类型,且在特征声明中没有显式地给出拥有的子集或者拥有的重定义, 那么该特征就隐式地作为Base库模型中的特征dataValues的一个子集。

    datatype IdNumber specializes ScalarValues::Integer;
datatype Reading { // 默认是 Base::DataValue 的子类
feature sensorId : IdNumber; // 默认是 Base::dataValues 的子集
feature value : ScalarValues::Real;
}

7.4.3 类

类(class)是对实存(occurrence)进行分类的分类器,即在时间和空间中存在的事物。 实存和其他事物之间的关系可以随着时空变化而变化,而实存本身则保持了独立的一致性。

类使用关键class声明为一个分类器。 如果没有显式地给出父类,那么Occurrences库模型中的Occurrence类将隐式地作为它的父类。

如果一个特征的类型有任一个是类,那么它的所有类型都必须是类。 如果一个特征具有类作为类型,且在特征声明中没有显式地给出拥有的子集或者拥有的重定义, 那么该特征就隐式地作为Occurrences库模型中的特征occurrence的子集, 除非它的类型中至少有个是关联结构,这种情况的默认子类在7.4.5中详述。

    class Situation { // 默认特化 Occurrences::Occurrence
feature condition : ConditionCode;
feature soundAlarm : ScalarValues::Boolean;
}

class SituationStatusMonitor specializes StatusMonitor {
feature currentSituation[*] : Situation; // 默认是 Occurrences::occurrences 的子集
}

7.4.4 结构

结构(structure)是把对象(objects)进行分类的类,属于实存的一种。 结构通常限制了它们的实例以及之间的关系随时间发生变化的方式,而行为是表明了对象和之间的关系是如何变化的。

结构使用关键字struct声明为一个分类器。其默认父类是Objects库模型中的结构Object

如果一个特征的任一类型是结构,那么它的所有类型都必须是结构。 如果一个特征的类型是结构,且在特征声明中没有显式地给出拥有的子集或者拥有的重定义, 那么该特征就隐式地作为Objects库中的特征objects的子集, 除非至少有一个类型是关联结构,此种情况详见7.4.5.

    stuct Sensor { // 默认特化 Objects::Object
feature id : IdNumber;
feature currentReading : ScalarValues::Real;
step updateReading { ... }
}
struct SensorAssembly specializes Assembly {
composite feature sensors[*] : Sensor; // 默认是 Objects::objects 的子集
}

7.4.5 关联

关联(association)是对事物之间的连接(link)进行分类的分类器。 关联必须至少有两个拥有的特征是端特征,即关联端(association end), 用来指定每个连接所连接的事物(每个端一个事物,两端可能是同一个事物)。 只具有两个关联端的关联称为二元关联(binary association)。 关联也可以拥有不是端特征的特征,用来区分它所连接的事物的关联的实例。

关联使用关键字assoc声明为一个分类器。 其默认的父类或者是BinaryLink关联(如果是二元关联),或者是Link关联(非二元关联),二者都来自于Links库模型。

关联也是它的关联端的类型之间的一种关系,通过它的相关类型(related types)来指定。 连接存在于一个关联的相关类型的实例之间。 对于二元关联,两个相关类型被认为是源类型(source type)目标类型(target),二者可能相同。 具有多个关联端(n元)的关联只有目标类型,没有源类型。

重数的语义对于端特征和对于非端特征不同。 关联的端特征决定了连接中的参与者(participants),因此,实际上相对于关联的重数为1。 但是,如果一个关联端的重数不是1..1,那么其含义如下: 对于每一个关联端,TODO:

如果一个关联具有一个关联作为其单一父类,它可以从这个父类中继承关联端。 然而,如果其声明了任何所拥有的关联端,那么每一个关联端必须按顺序重定义父类中的一个关联端, 直到和父类中的关联端的数量相同。 如果对于拥有的关联端没有显式地指定重定义,那么认为它隐式地重定义了父类中位于同样位置的关联端。

    assoc Ownership { // 默认特化了 Links::BinaryLink
feature valuationOnPurchase : MonetaryValue;
end feature owner[1..*] : LegalEntity; // 重定义了 BinaryLink::source
end feature ownedAsset[*] : Asset; // 重定义了 BinaryLink:target
}
assoc SoleOwnership specializes Ownership {
end feature owner[1]; // 重定义了 Ownership::owner
// 继承了 ownedAsset
}

如果一个关联具有多个类型是关联的父类,那么该关联必须定义至少和它的父类中拥有关联端的最大数量相同的关联端。 每个关联端必须对每一个父类中位于相同位置的关联端进行重定义。

关联结构(association structure)既是关联,又是类,用来对连接对象(link objects)进行分类。 作为对象,连接对象可以创建,也可以销毁,它们的非端特征可以随时间而变化。 然而,连接对象的端特征的值是固定的(是只读的),在整个生命周期内不能变化。

关联特征的声明和一般的关联类似,但是使用关键字assoc struct。 关联结构必须直接或间接地特化基础关联结构LinkObject。 如果在声明中显式地通过ownedSuperclassifications给出了父类,TODO: 上面所述的对于关联端的规则也同样适用于关联结构。 关联结构可以特化一个不是关联结构的关联,但是关联结构的所有子类必须是关联结构。

    // 隐式地特化了 Objects::BinaryLinkObject
assoc struct ExtendedOwnership specializes Ownership {
//
feature revaluations[*] ordered : MonetaryValue;
}

如果一个特征具有一个或多个关联作为类型,那么这些关联必须具有相同数量的关联端。 TODO:

如果

7.4.7 行为

7.4.7.1 行为概览

行为(behavior)是对表现(performance)进行分类的类, 表现是可以在时空的不相连的组分中进行扩散的实存。 行为的表现可以导致其他事物上出现影响,包括包括它们的存在以及关系, 某些影响可以作为行为的输入被接受,或者作为输出提供出去。

行为可以具有步骤(step);步骤是以行为作为类型的特征,允许包含的行为和其他行为的表现进行协作。 步骤可以使用次序连接器表示时间上的先后顺序, 也可以使用项目流来表示一个步骤的输出到另一个步骤的输入之间的流动。 步骤也可以嵌套其他的步骤,来增强或者重定义从其行为类型中继承下来的步骤。

7.4.7.2 行为声明

行为使用关键字behavior声明为一个分类器。 如果未显式给出父类,那么隐式地使用Performances库模型中的行为Performance作为父类。

在行为的内容体中定义的带有非空方向的特征被认为是该行为所拥有的参数(parameter)。 方向为in的特征是输入参数,方向为out的是输出参数, 方向为inout的既是输入又是输出参数。

    // 默认特化 Performances::Performance
behavior TakePicture {
in scene : Scene;
out picture : Picture;
}

参数的顺序和其在行为内容体中的声明的顺序是一致的,它们可以出现在内容体重的任意位置。

如果行为具有拥有的子分类,其中的父类是行为, 那么该行为的每一个拥有的参数必须按顺序重定义每一个父类行为中位于同样位置的参数。 重定义参数应该和被重定义的参数具有相同的方向。

    behavior A { in a1; out a2;}
behavior B { in b1; out b2;}
behavior C specializes A, B {
in c1 redefines a1, b1;
out c2 redefines a2, b2;
}

如果行为具有单一的行为父类,那么该子类行为可以声明比父类行为的参数数量更少的拥有的参数, 而从父类继承其他的参数(在拥有的参数后面按顺序继承)。 如果有多个行为父类,那么每一个父类中的每一个参数都必须被子类的拥有的参数所重定义。 如果每一个每一个父类的参数已被重定义,那么该子类行为也可以定义额外的参数, 排在重定义参数之后。 如果对参数没有显式地给出重定义,那么就会隐式地创建一个拥有的重定义,来重定义父类参数,以满足上述规则。

    behavior A1 :> A { in aa; } // aa 重定义了 A::a1, A::a2 被继承下来  
behavior B1 :> B { in b1; out b2; inout b3; } // 隐式地重定义
behavior C1 :> A1, B1 { in c1; out c2; inout c3; }

在行为的内容体中定义的步骤是行为的所拥有的步骤。 行为也可以从父类行为中继承或者冲动已非私有步骤。

    behavior Focus { in scene : Scene; out image : Image; }
behavior Shoot { in image : Image; out picture : Picture; }
behavior TakePicture {
in scene : Scene;
out picture : Picture;
composite step focus : Focus;
composite step shoot : Shoot;
}

尽管行为的表现随时间而发生,但是步骤的声明顺序对于步骤在时间上的表现顺序没有任何影响。 步骤的时间顺序,或者之间的连接,必须显式地指定。

    behavior TakePicture {
in scene : Scene;
out picture : Picture;

binding focus.scene = scene;
composite step focus : Focus;
succession focus then shoot;
composite flow focus.image to shoot.image;
composite step shoot : Shoot;
binding picture = focus.picture;
}

7.4.7.3 步骤声明

步骤通过关键字step声明为一个特征。 默认作为Performances库模型中的特征performances的子集。

作为一个行为,在步骤的内容体中声明的有向特征被看做是步骤的参数。 如果步骤所拥有的特化(包括所有的特征赋型,子集,重定义)的一般类型是行为或者步骤, 那么这个行为或者步骤的参数的重定义规则和子类行为对父类行为的参数进行重定义的规则一致。

    step focus : Focus {
// 重定义了Focus中的参数
in scene;
out image;
}
// 参数被隐式地继承
step refocus subsets focus;

步骤可以具有内容体,其中可以包含其他的步骤。 步骤可以对它的行为类型或者它的父集步骤中的步骤进行继承或者重定义。

    step takePictureWithAutoFocus : TakePicture {
in feature unfocusedScene redefines scene;
step redefines focus : AutoFocus;
out feature focusedPicture redefines picture;
}

7.4.8 函数

7.4.8.1 函数概述

函数(function)是具有一个输出参数(称为结果参数)的行为。 函数对求解(evaluation) 进行分类,求解是表现的一种,其产生作为结果参数的结果(result)值。 和所有行为一样,函数可以改变事物,称为副作用(side effects)。 没有副作用并且在给定输入下总是产生相同结果的函数称为纯(pure)函数,和数学意义上的函数一样。 例如,在Kernel Function Library 中的数值函数都是纯函数。

表达式(expression)是由单一函数作为类型的步骤,意味着它们的值是求解。 如果一个表达式的值是一个具有结果的求解,那么称为求解为(evaluate to)这些结果。 表达式可以作为任意行为的步骤,但是在函数中,可以指定其中一个表达式步骤作为结果表达式(result expression), 给出结果参数的值。 表达式可以自己的内嵌参数,以增强或者重定义对应的函数中的参数,包括结果参数。 表达式也可以拥有其他的表达式,并且和函数一样指定一个结果表达式。

断言(predicate)是结果为一个布尔值(真或假)的函数。 断言在求解时,判断其输入参数是否满足特定的条件,如果满足,返回真,否则返回假。 断言对布尔求解(boolean evaluation)进行分类,布尔求解是返回布尔值的特殊求解。

布尔表达式(boolean expression)是函数类型为断言的表达式,因此求解为一个布尔值。 布尔表达式一般会有时求解为真,有时求解为假。 但是一个不变式(invariant)是一种总是求解为真或者假的布尔表达式。 默认情况下,一个不变式总是求解为真,而一个反不变式(negate invariant)总是求解为假。

7.4.8.2 函数声明

TODO:

    function Velocity {
in v_i : VelocityValue;
in a : AccelerationValue;
in dt : TimeValue;
return v_f : VelocityValue;
}

7.4.8.3 表达式声明

TODO:

7.4.8.4 断言声明

    predicate isAssembled {
in assembly : Assembly;
in subassemblies[*] : Assembly;
}
    predicate isFull {
in tank : FuelTank;
tank.fuelLevel == tank.maxFuelLevel;
}

TODO:

7.4.8.5 布尔表达式和不变式声明

TODO:

    class FuelTank {
feature fuelLevel : Real;
feature readonly maxFuelLevel : Real;
bool isFull { fuelLevel == maxFuelLevel }
}
    class FuelTank {
feature fuelLevel : Real;
feature readonly maxFuelLevel : Real;
inv { fuelLevel >= 0 & fuelLevel <= maxFuelLevel>}
}

7.4.9 表达式

7.4.9.1 表达式概述

如 7.4.8 中所述,表达式是类型为函数的步骤,7.4.8.3节中覆盖了一般的声明表达式的方法。 然而,表达式通常具有树状的组织结构,以表达式作为节点,每一个表达式的输入参数绑定到它每一个子表达式的结果上。 KerML具有大量的文本标记,用来构建表达式树,包括用在Kernel模型库中的函数中的传统的操作符记号。

  • 表达式树上的非叶节点是调用表达式(invocation expression), 这是一种将其他表达式(参数,argument表达式)的结果作为它的输入值的表达式, 依次作为它所调用的函数的输入参数。
  • 树的边是调用表达式的输入参数和它的参数表达式的结果之间的绑定连接器。
  • 叶子节点是以下几种表达式:
    • 特征引用表达式(feature reference expression) 求解为一个不属于表达式树一部分的一个引用特征的值。
    • 字面量表达式(literal expression)求解为一个字面量值,类型为ScalarValues库模型中的基础数据类型。
    • 空值表达式(null expression) 求解为空集。

一个表达式也可以作为表达式中一个特征引用表达式中的引用。 这使得引用的表达式对应的求解本身可以作为调用的参数值,而不是将求解的结果(result)的值传递过去。 为实现上述用法的简写,表达式内容体的具象语法可以作为表达式语法书中的一个叶子节点。

一个模型层面可求解(model-level evaluable)的表达式是引用元数据的表达式, 元数据是关于模型元素而非所建模事物的数据。 模型层面可求解的表达式可以给出元数据特征的值,作为包中元素的过滤条件。 模型层面可求解的表达式的

  • 所有的空值表达式,字面量表达式和特征引用表达式都是模型层面可求解的。
  • 所有的调用表达式如果满足以下条件,则是模型层面可求解的:
    1. 所有参数表达式都是模型层面可求解的。
    2. 调用在表格5和表格7中列出的函数(这些函数是模型层面可求解的)。

7.4.9.2 操作符表达式

操作符表达式(operator expression)的书写形式为调用库函数中表示为一个操作符符号(oprator symbol)的表达式提供了简写形式。 TODO:

7.4.9.3 基础表达式

TODO:

7.4.9.4 base expression

TODO:

7.4.9.5 字面量表达式

字面量表达式(literal expression) 给顶一个词法上的字面量作为表达式的值。 TODO:

7.4.10 交互

7.4.10.1 交互概述

交互(interaction)既是关联,又是行为,用来对既是实存之间的连接又是performance 事物进行分类。 它们指明所连接的参与者如何相互影响和协作。

转换(transfer)是在两个参与者之间从一个实存到另一实存转移项目的交互, 这些项目可以分别由源和目标实存的输出和输入特征指定。

项目流(item flow)既是二元连接器,又是步骤,它们的值是转换。 项目流有时可以确保项目从连接的源特征的输出特征到目标特征的输入特征进行传输。 次序项目流(succession item flow)既是次序连接器,又是项目流, 表明转移发生在源之后(即发生在项目来源实存结束之后),而在目标之前(即发生在项目流向实存开始之前)。

7.4.10.2 交互声明

交互使用关键字interaction声明为一个行为。 其默认子类为Performances库模型中的Performance,以及Links库模型中的关联BinaryLink或者Link, 取决于它是否是二元交互。

作为一种行为,如果交互有拥有的子分类,且子分类中的父类是行为, 那么它们的参数相关的规则和子类行为一样。 作为一种关联,交互的内容体中必须声明至少两个关联端。 如果交互所拥有的父类中,有父类是关联的,那么它们的关联端相关的规则和一样。

    interaction Authorization {
end feature client[*] : Computer;
end feature server[*] : Computer;
composite step login;
composite step authorize;
composite succession login then authorize;
}

7.4.10.3 项目流声明

TODO:

    struct Vehicle {
composite feature fuelTank[1] {
out featur fuelOut[1] : Fuel;
}
composite feature engine {
in feature fuelIn[1] : Fuel;
}
flow fuelFlow from fuelTank::fuelOut to engine::fuelIn;
}

项目流的声明也可以在关键字of后面显式地包括所传输的项目的类型和/或重数。 这表明该项目流传输的所有项目都具有所声明的类型。 如果没有项目的声明,任何和源的输出以及目标的输入特征的类型相一致的值,都可以通过该项目流传输。

    flow of flowingFuel : Fuel from fuelTank.fuelOut to engine.fuelIn;

TODO:

7.4.11 特征值

特征值(feature value)是在一个所属特征和一个值表达式(value expression)之间的从属关系, 其中值表达式的结果提供了此特征的值。 特征值关系可以是bound或者initial,同时,也可以是concrete或者default。 一个特征最多只能有一个特征值关系。

一个确定的、绑定的特征值关系通过在符号=后面加上使用具象语法书写的值表达式来声明。 这些记号附加在特征值所拥有的特征的声明之后。

    feature monthsInYear : Natural = 12;
struct TestRecord {
feature scores[1..*] : Integer;
derived feature averageScore[1] : Rational = sum(scores)/size(scores);
}

具有这种形式的特征值关系的特征,都隐式地有一个在特征和值表达式的结果之间的内嵌绑定连接器, 该绑定连接器的表征类型和所声明的特征一样。

注意:绑定的语义意味着这种特征值表明了一个特征和值表达式的结果是相等的(equivalent)。 为了强调这一点,具有这种特征值的特征可以标记为导出的(derived) (尽管这不是必须的,而且一个导出的特征值使用一个特征值计算得到也不是必须的)。

一个确定的、初始的特征值关系和上述的声明方式一样,只是使用符号:=代替=

   feature count[1] : Natural := 0;

这种情况下,这个特征也有一个隐式地内嵌绑定连接器, 但是绑定连接器的表征类型是所声明特征的表征类型的起始快照(starting snapshot)。 也就是说,值表达式的结果作为声明特征的初始值,但是和绑定值的情况不同, 这些初始值后续可能发生变化。

默认特征值关系的声明和上述类似,但是使用关键字default。根据是绑定值还是初始值, 分别跟在符号=或者:=后面。 但是,对于绑定特征值,default后面的=可以省略。

    struct Vehicle {
feature mass[1] : Real default 1500.0;
feature engine[1] : Engine default := standardEngine;
}
struct TestWithCutoff :> TestRecord {
feature cutoff[1] : Rational default = 0.75 * averageScore;
}

对于默认特征值关系,不会在特征声明中添加绑定连接器, 但是在表征类型的实例构造的时候,如果没有显式地给定该特征一个值,那么就会应用默认值。

TODO:

7.4.12 重数

重数是定义在Core层的一个特征,用来指定一个类型的基数(实例的数量), 这种指定是通过将所有可能的基数数量枚举出来实现的。 Kernel层提供了一种特定的方式来指定基数,即通过基数的范围(range)。 重数的范围具有下界(lower bound)上界(upper bound) 表达式, 求解出的值分别确定最低和最高的基数,这两个值都是自然数(即ScalarValues库模型中的类型Natural)。 上界值为*(无穷)意味着基数包括所有大于或者等于下界值的数字。

重数范围的形式为[lowerBound..upperBound],其中lowerBound和upperBound或者是 一个字面量表达式,或者是一个特征引用表达式。 字面量表达式可以用来指定具有固定下界和/或上界的重数范围。 如果lowerBound表达式的结果是*,那么这个重数范围的含义是未确定的。

重数猚也可以不写下界(或者..)。 这种情况下,单个的表达式的结果同时作为下界和上界, 除非结果是无限值*,那么下界被认为是0。

重数范围可以用在类型,特别是特征,的声明中。

    struct Automobile {
feature n : Positive[1];
composite feature wheels : Wheel[n];
feature driveWheels[2..n] subsets wheels;
}
feature autoCollection : Automobile[*];

也可以使用关键字multiplicity来声明一个重数特征,后面可以跟着简称和/或名称, 并且包括重数范围或者另外一个重数的子集。 重数的声明是一种特征声明,也可以像一般的特征声明一样包括内容体。

    multiplicity zeroOrMore [0..*];
multiplicity m subsets zeroOrMore;

如果重数特征声明在类型的内容体中,那么就自动称为该类型的重数。 一个类型最多有一个重数,不管是在声明还是在内容体中给出。

    feature driveWheels subsets wheels {
multiplicity [2..n];
}
feature autoCollection {
multiplicity subsets zeroOrMore;
}

7.4.13 元数据

元数据(metadata)是模型元素上的额外信息,它不具有任何实例层面的语义。 一般地,元数据在附着到被注解元素上的注解元素(包括注释和文本表示)中指定。 元数据特征(metadata feature)是一种注解元素, 允许定义带有建模者指定的特征的结构化的元数据。 例如,这可以用来对模型添加工具特定的信息,以支持不同种类的工具链来使用或者处理模型的功能, 或者添加和某个项目或者组织相关的领域特定的信息。

元数据特征从语法上来看,是一个由单一的元类(Metaclass)作为类型的特征, 属于结构的一种,其隐式地具有重数1..1。 如果元类没有特征,那么元数据特征仅是作为一个用在所注解元素上的用户定义的语法标签。 如果元类有特征,那么元数据特征必须具有内嵌特征,用来对其类型中的特征进行一一重定义, 把它们和模型层面可求解的表达式的结果绑定起来。这些模型层面可求解的表达式提供了指定的属性元数据。

元类使用关键字metaclass来声明,类似于结构。 其默认父类为Metaobjects库模型中的元类Metaobject

     metaclass SecurityRelated;

metaclass ApprovalAnnotation {
feature approved[1] : Boolean;
feature approver[1] : String;
}

元数据特征使用关键字metadata(或者符号@)来声明,后面可选的跟着简称和/或名称, 然后跟着关键字typed by(或者符号:)以及恰好一个元类的限定名称。 如果未给出简称或者名称,那么关键字typed by(或者符号:)可以省略。 然后是在关键字about后面跟着一个或者多个被注解的元素, 表示该元数据特征和每一个指定的元素都有注解关系。

    metadata securityDesignAnnotation : SecurityRelated about SecurityDesign;

元数据特征的任一个拥有的特征必须重定义它所表征元类的特征, 其特征值绑定到一个模型层面可求解的表达式的结果。 元数据特征所拥有的特征必须总是和其所表征元类的特征的名称相同, 这样可以总是使用重定义的简写形式。

    metadata ApprovalAnnotation about Design {
feature redefines approved = true;
feature redefines approver = "John Smith";
}

关键字feature和/或redefines(或者等价的符号:>>)在元数据特征的声明中可以省略。

    metadata ApprovalAnnotation about Design {
approved = true;
approver = "John Smith";
}

如果元数据特征是一个命名空间的拥有的成员,那么可以不显示地指定所注解的元素(跟在about关键字后的元素), 这种情况下containing 命名空间隐式地称为所注解的元素。

    class Design {
@ApprovalAnnotation {
approved = true;
approver = "John Smith";
}
}

如果一个元数据特征有一个或多个具象特征,且这些特征直接或间接地是Metaobject::annotatedElement的子集, 那么对于每一个该元数据特征所注解的元素,应该至少存在一个这样的特征, 以使所注解元素的元类和该特征的所有类型相兼容。

特征可能有多个类型,即所注解元素的元类应该和元数据特征的属于Metaobject::annotatedElement 的子集的特征的类型兼容。

    metaclass Command {
// 该元类的元数据特征可以用来注解行为或者步骤
subsets annotatedElement : KerML::Behavior;
subsets annotatedElement : KerML::Step;
}

behavior Save specializes UserAction {
@Command;
redefine step doAction {
@Command;
}
}
struct Options {
@Command; // 无效
}

如果元数据特征的元类是Metaobjects::SemanticMetadata的一个直接或间接特化, 那么被注解的元素必须是类型,且特征SemanticMetadata::baseType必须绑定到一个KerML::Type的值。 被这样的语义元数据所注解的类型,具有一个隐式地特化,其规则根据baseType的值来确定:

  • 如果被注解的类型既不是分类器也不是特征,那么被注解的类型隐式地特化baseType
  • 如果被注解的类型是分类器,且baseType是分类器,那么被注解的分类器隐式地subclassifies baseType
  • 如果被注解的类型是分类器,且baseType是特征,那么被注解的分类器隐式地subclassifies baseType的每一个类型。
  • 如果被注解的类型是特征,且baseType是特征,那么被注解的特征应该隐式地作为baseType的子集。
  • 其他情况下,没有隐式的特化。

当在一个模型层面可求解的表达式中求解时,元转换操作符meta可以用来将一个作为其第一个操作数的特征 转换为该特征的实际反射的元类的值,这个值又可以绑定到SemanticmetadatabaseType特征上。

    behavior UserAction;
step userActions : UserAction[*] nonunique;

metaclass Command specializes SemanticMetadata {
// 转换操作 `userActions meta KerML::Feature`中,
// 类型是 KerML::Feature,这和 baseType的类型一致。
// 由于 userAction 是一个步骤,该表达式最终求解的结果为
// KerML::Step 的一个值。
// baseType = UserAction;
baseType = userActions meta KerML::Feature;
}

behavior Save {
@Command;
}

step previousAction[1] {
@Command;
}

自定义关键字

自定义关键字(user-defined keyword)是一个前缀以符号#的元类的名称或者简称(可能包含限定)。 自定义关键字写在语言定义(保留)关键字之前,用于声明, 并指明所声明的元素的一个元数据特征注解。 如果元类是一个SemanticMetadata,那么同样隐式地应用上述的语义元数据的规则。

    metaclass <command> CommandMetadata :> SemanticMetadata {
baseType = userActions meta KerML::Feature;
}

#command behavior Save;
#command step previousAction[1];

在一个声明中可以添加多个自定义关键字。

    #SecurityRelated #command def Save;

7.4.14 包

包(Package)是用来对元素进行分组的命名空间,没有任何实例层面的语义 (和类型相反,其是具有分类语义的命名空间)。 包的写法和普通的命名空间类似,但是使用关键字package代替namespace

    package AddressBooks {
datatype Entry {
feature name[1] : String;
feature address[1] : String;
}
struct AddressBook {
composite feature entries[*]: Entry;
}
}

包可以有一个或多个过滤条件(filter condition),用来选取其引入从属关系的子集。 过滤条件是一个结果为布尔值的模型层面可求解的表达式,如果。 写法为使用关键字filter后面跟着过滤条件表达式。

    package Annotations {
metaclass ApprovalAnnotation {
feature approved[1] : Boolean;
feature approver[1] : String;
feature level[1] : Natural;
}
...
}
.TODO:

过滤条件可以在元素的元数据上执行,例如检查一个特定类型的元数据特征, 或者访问元数据特征的特征的值。 就过滤条件的目的而言,每一个元素也有一个隐式地元数据特征, 其类型是KerML抽象语法的反射库模型中的一个元类。 这使得过滤条件可以测试元素的抽象语法元类, 并且方位抽象语法元属性的值。

注意,过滤条件会对包的所有的引入元素进行过滤。

    package UpperLevelApprovals {
// 递归的引入所有的注解数据类型以及这些类型的所有特征
import Annotations::**;
// 这个过滤条件仅对从DesignModel包中引入的元素有效
import DesignModel::**[@ApprovalAnnotation and approved and level>1];
}

KerML包中,包含了使用KerML表示的完整的KerML抽象语法模型。 当一个过滤条件对元素求解时,可以测试元素的抽象语法元数据, 即认为元素具有一个隐式的元数据特征,类型为KerML包中对应于该元素的元类的类型。

    package PackageApprovals {
import Annotations::*;
import KerML::*;

import DesignModel::**[@Structure and
Structure::ownedFeature != null and
@ApprovalAnnotation and
ApprovalAnnotation::approved];
}

库(library)是一个包,表示这个包通常是可用的,并且在许多用户模型中被重用。 使用关键字library可以将一个包标记为库。 这使得工具可以将直接包含在库中的元素处理为库元素(library element)

    library package AddressBooks {
...
}

Kernel模型库中的标准库进一步被关键字standard标记。 但是,只有在Kernel模型库,以及其他被认可的标准模型库中的库才应该被标记为标准库。