blob: 6d38c3245e464df1e90127e0ca440a1b2e5d31cb [file] [log] [blame]
import 'Pivot.ecore'
include 'Types.ocl'
package ocl
context Constraint
-- Constraints should have a Boolean value (except for legacy Eclipse OCL purposes it may be null, for legacy UML 2.5 purposes it may be OclVoid).
inv BooleanValued: ownedSpecification <> null and ownedSpecification.type <> null implies ownedSpecification.type = Boolean or ownedSpecification.type = OclVoid
inv UniqueName: true -- _'context'.ownedRule->excluding(self)->forAll(name <> self.name or stereotype <> self.stereotype)
context Element
def: allOwnedElements() : Set(Element) =
self->closure(oclContents()->selectByKind(Element))
--context Element
--def: formatNames(prefix : String, names : Set(NamedElement[*|1])) : String = names->sortedBy(name)->iterate(p; acc:String=prefix|acc +' ' + p.name)
--FIXME oclContainer() suspect on DomainElements
--def: owner : Element = oclContainer()
/**
* Elements that must be owned must have an owner.
*/
-- inv has_owner:
-- mustBeOwned() implies owner->notEmpty()
/**
* An element may not directly or indirectly own itself.
*/
-- inv not_own_self:
-- not allOwnedElements()->includes(self)
-- def: allOwnedElements() : Set(Element) =
-- ownedElement->union(ownedElement->collect(e | e.allOwnedElements()))->asSet()
-- def: mustBeOwned() : Boolean = true
--context ParameterableElement
--def: isCompatibleWith(p : ParameterableElement) : Boolean =
--p.oclIsKindOf(self.oclType())
context Type
def: flattenedType() : Type[?] = self
--context CollectionType
--def: flattenedType() : Type = elementType.flattenedType()
context Property
def: isAttribute(p : Property) : Boolean =
--Type.allInstances()->exists(c| c.ownedAttribute->includes(p))
let container : ocl::OclElement = oclContainer() in container.oclIsKindOf(Class) and container.oclAsType(Class).ownedProperties->includes(self)
--inv DerivedIsInitialized: isDerived implies ownedRule->one(stereotype = 'derivation') or ownedRule->one(stereotype = 'initial')
--inv CompatibleInitializer: isDerived implies defaultExpression <> null
inv CompatibleDefaultExpression: ownedExpression <> null and ownedExpression.oclAsType(ExpressionInOCL).ownedBody <> null implies CompatibleBody(ownedExpression)
context OCLExpression
inv TypeIsNotNull : type <> null
context Operation
inv CompatibleReturn: bodyExpression <> null and bodyExpression.oclAsType(ExpressionInOCL).ownedBody <> null implies CompatibleBody(bodyExpression)
inv LoadableImplementation:
true
inv UniquePreconditionName: ownedPreconditions->isUnique(name)
inv UniquePostconditionName: ownedPostconditions->isUnique(name)
context Feature
inv NameIsNotNull : name <> null
inv TypeIsNotInvalid : type <> OclInvalid
inv TypeIsNotNull : type <> null
context CollectionLiteralPart
inv TypeIsNotInvalid : type <> OclInvalid
context CallExp
inv TypeIsNotInvalid : type <> OclInvalid
/** Safe navigation is not necessary when the source collection is null-free. */
inv SafeSourceCanBeNull: isSafe implies not ownedSource?.type.oclAsType(CollectionType).isNullFree
/** Safe navigation is not supported when the source collection is a Map. */
inv SafeSourceCannotBeMap: isSafe implies let sourceType = ownedSource?.type in sourceType <> null implies not sourceType.oclIsKindOf(MapType)
context IfExp
inv TypeIsNotInvalid : type <> OclInvalid
context LetExp
inv TypeIsNotInvalid : type <> OclInvalid
context Class
inv NameIsNotNull : name <> null
inv UniqueInvariantName: ownedInvariants->isUnique(name)
context IteratorExp
inv ClosureBodyTypeIsConformanttoIteratorType:
true -- FIXME obsolete
inv SortedByIteratorTypeIsComparable:
true
context TypedElement
def: CompatibleBody(bodySpecification : ValueSpecification[1]) : Boolean[1] =
bodySpecification.type?.conformsTo(self.type)
--def: makeParameter() : Parameter = Parameter{name='name'}
---ExpressionWFRs.ocl-----
--context PropertyCallExp
/** The type of the call expression is the type of the referred property. */
--inv TypeIsReferredPropertyType: type = referredProperty.type
context BooleanLiteralExp
/** The type of a boolean Literal expression is the type Boolean. */
inv TypeIsBoolean: self.type = Boolean
context CollectionLiteralExp
/** 'Collection' is an abstract class on the M1 level and has no M0 instances. */
inv CollectionKindIsConcrete: kind <> CollectionKind::Collection
context CollectionLiteralExp
/** The type of a collection literal expression is determined by the collection kind selection and the common
supertype of all elements. Note that the definition below defines only an upper bound on the elementType. The usage of
the CollectionLiteralExp defines a lower bound. If the elementType is not explicitly specified, the elementType must be
chosen to ensure the well-formedness of the elements of the CollectionLiteralExp and the usage of the
CollectionLiteralExp.
For instance in
acc : Set(Real) = Set{1}->excluding(-1)
Set{1} is well formed for any type Set(T) where T ≤ UnlimitedNatural. Well-formedness of the excluding operation call
requires T ≤ Integer, and well-formedness of the initializer requires Real ≤ T. The overall expression is therefore only
well-formed if Real ≤ T ≤ Integer. Either Set(Real) or Set(Integer) are well-formed. The most general type, Set(Real), is
recommended since it minimizes type conversions and can often be easily deduced by considering the result type.
*/
inv SetKindIsSet: kind = CollectionKind::Set implies type.oclIsKindOf(SetType)
inv OrderedSetKindIsOrderedSet: kind = CollectionKind::OrderedSet implies type.oclIsKindOf(OrderedSetType)
inv SequenceKindIsSequence: kind = CollectionKind::Sequence implies type.oclIsKindOf(SequenceType)
inv BagKindIsBag: kind = CollectionKind::Bag implies type.oclIsKindOf(BagType)
--inv ElementTypeIsCommonElementType: let elementType : Type = part->iterate (p; c : Classifier = OclVoid | c.commonSuperType (p.type))
--in elementType.conformsTo(type.oclAsType(CollectionType).elementType)
/* CollectionLiteralPart
No additional well-formedness rules. */
context CollectionItem
/** The type of a CollectionItem is the type of the item expression. */
inv TypeIsItemType: type = ownedItem.type
--context CollectionRange
/** The type of a CollectionRange is the common supertype of the expressions taking part in the range. */
--inv TypeIsCommonType: type = first.type.commonSuperType (last.type)
context EnumLiteralExp
/** The type of an enum Literal expression is the type of the referred literal. */
inv TypeIsEnumerationType: self.type = referredLiteral?.owningEnumeration
context IfExp
/** The type of the condition of an if expression must be Boolean. */
inv ConditionTypeIsBoolean: self.ownedCondition.type = Boolean
/* The type of the if expression is the most common supertype of the else and then expressions. */
--context IfExp
--inv TypeIsCommonType: self.type = thenExpression.type.commonSuperType(elseExpression.type)
context IntegerLiteralExp
/** The type of an integer Literal expression is the type Integer. */
inv TypeIsInteger: self.type = Integer
context IteratorExp
inv AnyHasOneIterator: true -- FIXME obsolete
inv AnyTypeIsSourceElementType: true -- FIXME obsolete
inv AnyBodyTypeIsBoolean: true -- FIXME obsolete
inv ClosureHasOneIterator: true -- FIXME obsolete
context IteratorExp
/** The collection type for an OrderedSet or a Sequence source type is OrderedSet.
* For any other source the collection type is Set. */
inv ClosureTypeIsUniqueCollection: name = 'closure' implies
if ownedSource?.type?.oclIsKindOf(SequenceType) or ownedSource?.type.oclIsKindOf(OrderedSetType) then
type.oclIsKindOf(OrderedSetType)
else
type.oclIsKindOf(SetType)
endif
context IteratorExp
inv ClosureSourceElementTypeIsBodyElementType: true -- FIXME obsolete
/** The result element type is the same as type of the body elements or element. */
inv ClosureBodyElementTypeIsIteratorType: name = 'closure' implies
let bodyElementType = if ownedBody.type.oclIsKindOf(CollectionType) then ownedBody.type.oclAsType(CollectionType).elementType elseif ownedBody.type.oclIsKindOf(MapType) then ownedBody.type.oclAsType(MapType).keyType else ownedBody.type endif in
let iteratorType = ownedIterators->at(1).type in
bodyElementType?.conformsTo(iteratorType)
context IteratorExp
/** The element type is the same as the source element type. */
inv ClosureElementTypeIsSourceElementType: true -- FIXME obsolete
/** Each body element is assignable to the iterator. */
inv ClosureResultElementTypeIsIteratorType:
name = 'closure' implies
let resultElementType = type.oclAsType(CollectionType).elementType in
let iteratorType = ownedIterators->at(1).type in
iteratorType?.conformsTo(resultElementType)
context IteratorExp
/* The collection type for an OrderedSet or a Sequence type is a Sequence,
* the result type for any other collection type is a Bag.
*/
inv CollectTypeIsUnordered: name = 'collect' implies
if ownedSource?.type.oclIsKindOf(SequenceType) or ownedSource?.type.oclIsKindOf(OrderedSetType) then
type.oclIsKindOf(SequenceType)
else
type.oclIsKindOf(BagType)
endif
context IteratorExp
/** The element type is the flattened type of the body elements. */
inv CollectElementTypeIsFlattenedBodyType: name = 'collect' implies
type.oclAsType(CollectionType).elementType = ownedBody.type?.flattenedType()
context IteratorExp
/** The collection type for an OrderedSet or a Sequence type is a Sequence, the result type for any other collection type is Bag. */
inv SortedByIsOrderedIfSourceIsOrdered: name = 'sortedBy' implies
if ownedSource?.type.oclIsKindOf(SequenceType) or ownedSource?.type.oclIsKindOf(BagType) then
type.oclIsKindOf(SequenceType)
else
type.oclIsKindOf(OrderedSetType)
endif
context IteratorExp
/** The element type is the type of the body elements. */
inv SortedByElementTypeIsSourceElementType: name = 'sortedBy' implies
type.oclAsType(CollectionType).elementType =
ownedSource?.type.oclAsType(CollectionType).elementType
context IterateExp
/** The type of the iterate is the type of the result variable. */
inv TypeIsResultType: true -- FIXME obsolete
/** The type of the body expression must conform to the declared type of the result variable. */
inv BodyTypeConformsToResultType: true -- FIXME obsolete
/** A result variable must have an init expression. */
inv OneInitializer: true -- FIXME obsolete
context LetExp
/** The type of a Let expression is the type of the in expression. */
inv TypeIsInType: type = ownedIn.type
/** The nullity of a Let expression is the nullity of the in expression. */
inv CompatibleNullityForIn: isRequired = ownedIn.isRequired
context LetVariable
/** Let variable has an initializer.*/
inv HasInitializer: ownedInit <> null
/** The type of a Let variable initializer expression conforms to the type of the Let variable. */
inv CompatibleTypeForInitializer: ownedInit <> null implies ownedInit.type?.conformsTo(type)
/** The nullity of a Let variable initializer expression is the nullity of the Let variable. */
inv CompatibleNullityForInitializer: ownedInit?.isRequired = isRequired
/** LiteralExp
No additional well-formedness rules. */
context LoopExp
/** Obsolete constraint replaced by SourceIsIterable. */
inv SourceIsCollection: true
/** The type of the source expression must be a collection or map. */
inv SourceIsIterable: ownedSource?.type.oclIsKindOf(IterableType)
/** The loop variable of an iterator expression has no init expression. */
inv NoInitializers: self.ownedIterators->forAll(ownedInit->isEmpty())
/** The loop variable of an iterator expression has no init expression. */
inv NoCoInitializers: self.ownedCoIterators->forAll(ownedInit->isEmpty())
/** A Map must have the same number of iterators and co-iterators. */
inv MatchingMapCoIterators: ownedSource?.type.oclIsKindOf(MapType) implies self.ownedCoIterators->size() = self.ownedIterators->size()
/** A Collection must have the same no co-iterators. */
inv NoCollectionCoIterators: ownedSource?.type.oclIsKindOf(CollectionType) implies self.ownedCoIterators->isEmpty()
context IteratorExp
/** The type of each collection iterator variable must be the type of the elements of the source collection. */
inv IteratorTypeIsSourceElementType: let sourceType = ownedSource?.type in sourceType.oclIsKindOf(CollectionType) implies
let sourceElementType = sourceType.oclAsType(CollectionType).elementType in
self.ownedIterators->forAll(p | sourceElementType.conformsTo(p.type))
/** The type of each map iterator variable must be the type of the keys of the source map. */
inv IteratorTypeIsSourceKeyType: let sourceType = ownedSource?.type in sourceType.oclIsKindOf(MapType) implies
let sourceKeyType = sourceType.oclAsType(MapType).keyType in
self.ownedIterators->forAll(p | sourceKeyType.conformsTo(p.type))
/** Safe navigation is not necessary when the source collection is null-free. */
inv SafeSourceCanBeNull: isSafe implies not
let sourceType = ownedSource?.type in
if sourceType.oclIsKindOf(MapType) then sourceType.oclAsType(MapType).keysAreNullFree else sourceType.oclAsType(CollectionType).isNullFree endif
/** Safe navigation is not necessary when an iterator can be null. */
inv SafeIteratorIsRequired: isSafe implies ownedIterators->forAll(isRequired)
/** Safe navigation is necessary when an iterator cannot be null and the source collection is not null-free. */
inv UnsafeSourceCanNotBeNull: (not isSafe and ownedIterators->exists(isRequired)) implies
let sourceType = ownedSource?.type in
if sourceType.oclIsKindOf(MapType) then sourceType.oclAsType(MapType).keysAreNullFree else sourceType.oclAsType(CollectionType).isNullFree endif
context IterateExp
/** Safe navigation is not necessary when the source collection is null-free. */
inv SafeSourceCanBeNull: isSafe implies not
let sourceType = ownedSource?.type in
if sourceType.oclIsKindOf(MapType) then sourceType.oclAsType(MapType).keysAreNullFree else sourceType.oclAsType(CollectionType).isNullFree endif
/** Safe navigation is not necessary when an iterator can be null. */
inv SafeIteratorIsRequired: isSafe implies ownedIterators->forAll(isRequired)
/** Safe navigation is necessary when an iterator cannot be null and the source collection is not null-free. */
inv UnsafeSourceCanNotBeNull: (not isSafe and ownedIterators->exists(isRequired)) implies
let sourceType = ownedSource?.type in
if sourceType.oclIsKindOf(MapType) then sourceType.oclAsType(MapType).keysAreNullFree else sourceType.oclAsType(CollectionType).isNullFree endif
context IteratorVariable
/** Iterator variable has no initializer.*/
inv HasNoInitializer: ownedInit = null
/** FeatureCallExp
No additional well-formedness rules. */
/** NumericLiteralExp
No additional well-formedness rules. */
/** OCLExpression
No additional well-formedness rules. */
--context MessageExp
/** If the message is an operation call action, the arguments must conform to the parameters of the operation. */
--inv: calledOperation->notEmpty() implies
--argument->forAll (a | a.type.conformsTo
--(self.calledOperation.operation.ownedParameter->
--select( kind = ParameterDirectionKind::_'in' )
--->at (argument->indexOf (a)).type))
--context MessageExp
/** If the message is a send signal action, the arguments must conform to the attributes of the signal. */
--inv: sentSignal->notEmpty() implies
--argument->forAll (a | a.type.conformsTo
--(self.sentSignal.signal.ownedAttribute
--->at (argument->indexOf (a)).type))
--context MessageExp
/** If the message is a call operation action, the operation must be an operation of the type of the target expression. */
--inv: calledOperation->notEmpty() implies
--target.type.allOperations()->includes(calledOperation.operation)
--context MapLiteralPart
--inv CompatibleTypeForInitializer: ownedInit <> null implies ownedInit?.type?.conformsTo(type)
--inv TypeIsNotInvalid : type <> OclInvalid
context MessageExp
/** An OCL message has either a called operation or a sent signal. */
inv OneCallOrOneSend: ownedCalledOperation->size() + ownedSentSignal->size() = 1
context MessageExp
/** The target of an OCL message cannot be a collection. */
inv TargetIsNotACollection: not ownedTarget.type.oclIsKindOf(CollectionType)
/*------------- spelling: arguments */
context OperationCallExp
/** All the arguments must conform to the parameters of the referred operation. */
inv ArgumentTypeIsConformant:
let operation : Operation = self.referredOperation in
let parameters : OrderedSet(Parameter) = operation?.ownedParameters in
let selfType : Type = operation?.owningClass in
Sequence{1..ownedArguments->size()}->forAll (i |
let argument : OCLExpression = ownedArguments->at(i) in
let parameter : Parameter = parameters?->at(i) in
let parameterType : Type = parameter.type in
let requiredType : Type = if parameter.isTypeof then Class else parameterType?.specializeIn(self, selfType) endif in
argument.type?.conformsTo(requiredType))
/*------------- spelling: arguments */
context OperationCallExp
def: hasOclVoidOverload() : Boolean[1] = false -- extended in Java code
/** There must be exactly as many arguments as the referred operation has parameters. */
inv ArgumentCount: ownedArguments->size() = referredOperation?.ownedParameters?->size()
/** Safe navigation is not necessary when the source cannot be null. */
inv SafeSourceCanBeNull: (ownedSource <> null) and isSafe implies not ownedSource.isNonNull()
/** Safe navigation is necessary when the source could be null. -- unless infix with an OclVoid overload */
inv UnsafeSourceCanNotBeNull: (not hasOclVoidOverload()) implies ((ownedSource <> null) and not isSafe implies ownedSource.isNonNull())
--inv StaticSourceIsNull: referredOperation.isStatic implies source = null
--inv NonStaticSourceIsConformant: let operation : Operation = self.referredOperation in
-- not operation.isStatic implies source <> null --.type.conformsTo(operation.owningClass.specializeIn(self, operation.owningClass))
--context OperationCallExp
/** An additional attribute refParams lists all parameters of the referred operation except the return and out parameter(s). */
--def: refParams: Sequence(Parameter) = referredOperation.ownedParameter->select (p |
--p.kind <> ParameterDirectionKind::return or
--p.kind <> ParameterDirectionKind::out)
context OppositePropertyCallExp
/** Safe navigation is not necessary when the source cannot be null. */
inv SafeSourceCanBeNull: (ownedSource <> null) and isSafe implies not ownedSource.isNonNull()
/** Safe navigation is necessary when the source could be null. */
inv UnsafeSourceCanNotBeNull: (ownedSource <> null) and not isSafe implies ownedSource.isNonNull()
context ParameterVariable
/** Parameter variable has no initializer.*/
inv HasNoInitializer: ownedInit = null
context PropertyCallExp
def: getSpecializedReferredPropertyOwningType() : Class = referredProperty?.owningClass -- extended in Java code
def: getSpecializedReferredPropertyType() : Class = referredProperty?.type.oclAsType(Class) -- extended in Java code
/** The type of the source conforms to the owning type of the property. */
inv NonStaticSourceTypeIsConformant: not referredProperty?.isStatic implies
ownedSource?.type?.conformsTo(getSpecializedReferredPropertyOwningType())
/** The type of the call expression is the type of the referred property. */
inv CompatibleResultType: type = getSpecializedReferredPropertyType()
/** Safe navigation is not necessary when the source cannot be null. */
inv SafeSourceCanBeNull: (ownedSource <> null) and isSafe implies not ownedSource.isNonNull()
/** Safe navigation is necessary when the source could be null. */
inv UnsafeSourceCanNotBeNull: (ownedSource <> null) and not isSafe implies ownedSource.isNonNull()
--inv StaticSourceIsNull: referredProperty.isStatic implies source.oclAsType(Type).conformsTo(getSpecializedReferredPropertyOwningType())
context ResultVariable
/** Result variable has an initializer.*/
inv HasInitializer: ownedInit <> null
/** The type of a Result variable initializer expression conforms to the type of the Result variable. */
inv CompatibleTypeForInitializer: ownedInit <> null implies ownedInit.type?.conformsTo(type)
/** A nullable expression cannot initialize a not-nullable Result variable. */
inv CompatibleNullityForInitializer: not ownedInit?.isRequired implies not isRequired
--context Class
--def: allProperties() : Set(Property[*|1]) = self->closure(superClasses)?.ownedProperties->asSet()
context ShadowExp
inv ClassHasNoStringValueInitializer: true -- obsolete
inv InitializesAllClassProperties: if type.oclIsKindOf(DataType) then Tuple{status:Boolean[1]=true, message:String[1]=''}.status else
let partProperties = ownedParts.referredProperty->asSet() in
let allProperties = type.oclAsType(Class)->closure(superClasses).ownedProperties->asSet() in
let classProperties = allProperties->reject(isDerived or isImplicit or isStatic or isTransient)->reject(name?.startsWith('ocl')) in
let requiredClassProperties = classProperties->reject(defaultValueString <> null)->reject(isVolatile or not isRequired)->reject(type.oclIsKindOf(CollectionType))->reject((opposite<>null) and opposite.isComposite) in
let extraProperties : Set(NamedElement[*|1]) = partProperties->excludingAll(classProperties) in
let missingProperties : Set(NamedElement[*|1]) = requiredClassProperties->excludingAll(partProperties) in
if extraProperties->notEmpty() then Tuple{status:Boolean[1]=false, message:String[1]=extraProperties->sortedBy(name)->iterate(p; acc:String='Unexpected initializers:'|acc +' ' + p.name)}.status
else if missingProperties->notEmpty() then Tuple{status:Boolean[1]=false, message:String[1]=missingProperties->sortedBy(name)->iterate(p; acc:String='Missing initializers:'|acc +' ' + p.name)}.status
else Tuple{status:Boolean[1]=true, message:String[1]=''}.status
endif endif endif
inv DataTypeHasStringValueInitializer: true -- obsolete
inv DataTypeHasNoPartInitializers: true -- obsolete
inv DataTypeHasOnePartInitializer: type.oclIsKindOf(DataType) implies ownedParts->size() = 1
inv TypeIsNotInvalid : type <> OclInvalid
context ShadowPart
inv CompatibleInitialiserType: ownedInit.type?.conformsTo(type)
inv TypeIsNotInvalid : type <> OclInvalid
inv TypeIsNotNull : type <> null
context StateExp
inv TypeIsNotInvalid : type <> OclInvalid
context TupleLiteralPart
inv CompatibleInitialiserType: ownedInit <> null and ownedInit.type <> null implies ownedInit.type.conformsTo(type)
inv TypeIsNotInvalid : type <> OclInvalid
context Variable
-- Variable semantics vary by consumer. Migrated to LetVariable, IteratorVariable, ParameterVariable, ResultVariable
inv CompatibleInitialiserType: true
context VariableDeclaration
inv NameIsNotNull : name <> null
inv TypeIsNotInvalid : type <> OclInvalid
inv TypeIsNotNull : type <> null
context VariableExp
inv TypeIsNotInvalid : type <> OclInvalid
context Element
def: getValue(stereotype : Type, propertyName : String) : Element[?] = null
/*
context CompleteModel
def: allRootPackageURIs = partialRoots.ownedPackages.completeURI->asSet()
inv: packageURI2completeURI.keys = allRootPackageURIs
inv: completeURI2packageURIs.keys->forAll(c | completeURI2packageURIs[c]->forAll(p | packageURI2completeURI[p] = c))
inv: packageURI2completeURI.keys->forAll(p | completeURI2packageURIs[packageURI2completeURI[p]]->includes(p))
context CompleteModel
def: allRootPackages : Set(Package) = partialRoots.ownedPackages->asSet() // no duplicates anyway
def: allRootPackageCompleteURIs = allRootPackages.completeURI->asSet()
inv: name2rootCompletePackage.keys = allRootPackageURIs
inv: name2rootCompletePackage.values = allRootPackages
inv: allRootPackageCompleteURIs->forAll(n | name2rootCompletePackage[n].partialPackages = allRootPackages->select(completeURI = n))
context CompletePackage
def: allNestedPackages : Set(Package) = partialPackages.ownedPackages->asSet() // no duplicates anyway
def: allNestedPackageNames = allNestedPackages.name->asSet()
inv: name2nestedCompletePackage.keys = allNestedPackageNames
inv: name2nestedCompletePackage.values = allNestedPackages
inv: allNestedPackageNames->forAll(n | name2nestedCompletePackage[n].partialPackages = allNestedPackages->select(name = n))
context NestedCompletePackage
def: allPackages : Set(Package) = owningCompletePackage.partialPackages.ownedPackages->asSet() // no duplicates anyway
def: allPackageNames = allPackages.name->asSet()
inv: name2completePackage.keys = allPackageNames
inv: name2completePackage.values = allPackages
inv: allPackageNames->forAll(n | name2completePackage[n].partialPackages = allPackages->select(name = n))
context CompleteClass
def: allClasses : Set(Class) = owningCompletePackage.partialPackages.ownedClasses->asSet() // no duplicates anyway
def: allClassNames = allClasses.name->asSet()
inv: name2completeClass.keys = allClassNames
inv: name2completeClass.values = allClasses
inv: allClassNames->forAll(n | name2completeClass[n].partialClasses = allClasses->select(name = n))
context CompleteModel
def: allClasses : Set(Class) = partialRoots->closure(ownedPackages).ownedClasses
inv: allClasses->forAll(c | class2completeClass[c].partialClasses->includes(c))
inv: allClasses->size() = class2completeClass.values.partialClasses->size()
inv: allClasses->size() = class2completeClass.values.partialClasses->asSet()->size()
*/
endpackage