diff --git a/src/utils/lib/helper.ts b/src/utils/lib/helper.ts index 63ac783..cae8d9a 100644 --- a/src/utils/lib/helper.ts +++ b/src/utils/lib/helper.ts @@ -25,6 +25,6 @@ export interface IXhr { response?: T; // z.B. Document, string, object, ... responseType?: XMLHttpRequestResponseType; // '' | 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' headers?: Record; - ok?: boolean; // convenience: status in 200-299 + ok?: boolean; // convenience: status == 0 or in the range of 200-299 } export const xhrOk:IXhr = { status: 0, ok: true }; diff --git a/src/utils/schemas/pig/pig-metaclasses.ts b/src/utils/schemas/pig/pig-metaclasses.ts index 009a117..3448e6e 100644 --- a/src/utils/schemas/pig/pig-metaclasses.ts +++ b/src/utils/schemas/pig/pig-metaclasses.ts @@ -16,9 +16,10 @@ * This means the code must resolve any reference by reading the referenced object explicitly from cache, when needed. * - To avoid access to the cache in the validation methods, the validation of references to classes shall be done in an overall consistency check * before the items are instantiated here. +* - programming errors result in exceptions, data errors in IXhr return values. */ -import { xhrOk } from "../../lib/helper"; +import { IXhr, xhrOk } from "../../lib/helper"; export type TPigId = string; // an IRI, typically a UUID with namespace (e.g. 'ns:123e4567-e89b-12d3-a456-426614174000') or a URL export type TRevision = string; // ToDo: should be better described using a pattern (RegExp) @@ -90,32 +91,40 @@ These capture who, what, when, where, and how of data access or changes: - Application / Client Used: Tool or program accessing the database -> (Agent oder Client App) - Success/Failure Status: Indicates whether the operation succeeded or failed -> in unserem Fall wohl überflüssig. */ -interface IIdentifiable { - id: TPigId; // translates to @id in JSON-LD +interface IItem { itemType: PigItemTypeValue; +} +abstract class Item implements IItem { + readonly itemType!: PigItemTypeValue; + protected lastStatus!: IXhr; + protected constructor(itm: IItem) { + this.itemType = itm.itemType; + } + status(): IXhr { + return this.lastStatus; + } +} +interface IIdentifiable extends IItem { + id: TPigId; // translates to @id in JSON-LD title: ILanguageText; description?: ILanguageText; } -abstract class Identifiable implements IIdentifiable { - readonly id!: TPigId; - readonly itemType!: PigItemTypeValue; +abstract class Identifiable extends Item implements IIdentifiable { + id!: TPigId; title!: ILanguageText; description?: ILanguageText; - constructor(itm: IIdentifiable) { - this.id = itm.id; - this.itemType = itm.itemType; - this.title = itm.title; - this.description = itm.description; + protected constructor(itm: IItem) { + super(itm); // actual itemType set in concrete class } - set(itm: IIdentifiable) { - if (itm.id !== this.id) - throw new Error(`Cannot change the id of an item (tried to change from ${this.id} to ${itm.id})`); - if (itm.itemType !== this.itemType) - throw new Error(`Cannot change the itemType of an item (tried to change from ${this.itemType} to ${itm.itemType})`); + protected set(itm: IIdentifiable) { + // validated in concrete subclass before calling this; + // also lastStatus set in concrete subclass. + this.id = itm.id; this.title = itm.title; this.description = itm.description; + // made chainable in concrete subclass } - get() { + protected get() { return { id: this.id, itemType: this.itemType, @@ -123,6 +132,14 @@ abstract class Identifiable implements IIdentifiable { description: this.description }; } + protected validate(itm: IIdentifiable) { + if (itm.itemType !== this.itemType) + throw new Error(`Cannot change the itemType of an item (tried to change from ${this.itemType} to ${itm.itemType})`); + if (this.id && itm.id !== this.id) + throw new Error(`Cannot change the id of an item (tried to change from ${this.id} to ${itm.id})`); + // ToDo: implement further validation logic + return xhrOk; + } } interface IElement extends IIdentifiable { @@ -130,26 +147,30 @@ interface IElement extends IIdentifiable { icon?: string; // optional, default is undefined (no icon) } abstract class Element extends Identifiable implements IElement { - eligibleProperty: TPigId[]; + eligibleProperty!: TPigId[]; icon?: string; - constructor(itm: IElement) { - super(itm); - this.eligibleProperty = itm.eligibleProperty || []; - this.icon = itm.icon; + protected constructor(itm: IItem) { + super(itm); // actual itemType set in concrete class } - set(itm: IElement) { + protected set(itm: IElement) { + // validated in concrete subclass before calling this; + // also lastStatus set in concrete subclass. super.set(itm); this.eligibleProperty = itm.eligibleProperty || []; this.icon = itm.icon; + // made chainable in concrete subclass } - get() { + protected get() { return { ...super.get(), eligibleProperty: this.eligibleProperty, - // eligibleProperty: this.eligibleProperty.map(p=>p.get()), icon: this.icon }; } + protected validate(itm: IElement) { + // ToDo: implement further validation logic + return super.validate(itm); + } } interface IAnElement extends IIdentifiable { @@ -164,20 +185,22 @@ abstract class AnElement extends Identifiable implements IAnElement { priorRevision?: TRevision[]; modified!: Date; creator?: string; - hasProperty: AProperty[]; // instantiated AProperty items - constructor(itm: IAnElement) { + hasProperty!: AProperty[]; // instantiated AProperty items + protected constructor(itm: IItem) { super(itm); - this.hasProperty = itm.hasProperty ? itm.hasProperty.map(i => new AProperty(i)) : []; } - set(itm: IAnElement) { + protected set(itm: IAnElement) { + // validated in concrete subclass before calling this; + // also lastStatus set in concrete subclass. super.set(itm); this.revision = itm.revision; this.priorRevision = itm.priorRevision; this.modified = itm.modified; this.creator = itm.creator; - this.hasProperty = itm.hasProperty ? itm.hasProperty.map(i => new AProperty(i)) : []; + this.hasProperty = itm.hasProperty ? itm.hasProperty.map(i => new AProperty().set(i)) : []; + // made chainable in concrete subclass } - get() { + protected get() { return { ...super.get(), revision: this.revision, @@ -187,25 +210,27 @@ abstract class AnElement extends Identifiable implements IAnElement { hasProperty: this.hasProperty.map(p => p.get()) }; } + protected validate(itm: IAnElement) { + // ToDo: implement further validation logic + return super.validate(itm); + } } ////////////////////////////////////// -// For the concrete classes: +// The concrete classes: export interface IProperty extends IIdentifiable { - specializes?: TPigId; // must be IRI of another Property, no cyclic references, translates to rdfs:subPropertyOf datatype: XsDataType; minCount?: number; maxCount?: number; - maxLength?: number; - pattern?: string; - minInclusive?: number; - maxInclusive?: number; + maxLength?: number; // only used for string datatype + pattern?: string; // a RegExp pattern, only used for string datatype + minInclusive?: number; // only used for numeric datatypes + maxInclusive?: number; // only used for numeric datatypes composedProperty?: TPigId[]; // must be IRI of another Property, no cyclic references - defaultValue?: string; + defaultValue?: string; // in PIG, values of all datatypes are strings } export class Property extends Identifiable implements IProperty { - readonly specializes?: TPigId; - datatype: XsDataType; + datatype!: XsDataType; minCount?: number; maxCount?: number; maxLength?: number; @@ -214,38 +239,33 @@ export class Property extends Identifiable implements IProperty { maxInclusive?: number; composedProperty?: TPigId[]; defaultValue?: string; - constructor(itm: IProperty) { - super(itm); - this.specializes = itm.specializes; - this.datatype = itm.datatype; - this.minCount = itm.minCount || 0; - this.maxCount = itm.maxCount || 1; - this.maxLength = itm.maxLength; - this.pattern = itm.pattern; - this.minInclusive = itm.minInclusive; - this.maxInclusive = itm.maxInclusive; - this.composedProperty = itm.composedProperty; - this.defaultValue = itm.defaultValue; - this.validate(); + constructor() { + super({itemType:PigItemType.Property}); } set(itm: IProperty) { - super.set(itm); - // do not allow changing 'specializes' after creation - this.datatype = itm.datatype; - this.minCount = itm.minCount || 0; - this.maxCount = itm.maxCount || 1; - this.maxLength = itm.maxLength; - this.pattern = itm.pattern; - this.minInclusive = itm.minInclusive; - this.maxInclusive = itm.maxInclusive; - this.composedProperty = itm.composedProperty; - this.defaultValue = itm.defaultValue; - return this.validate(); + this.lastStatus = this.validate(itm); + if (this.lastStatus) { + super.set(itm); + this.datatype = itm.datatype; + this.minCount = itm.minCount || 0; + this.maxCount = itm.maxCount || 1; + this.maxLength = itm.maxLength; + this.pattern = itm.pattern; + this.minInclusive = itm.minInclusive; + this.maxInclusive = itm.maxInclusive; + this.composedProperty = itm.composedProperty; + this.defaultValue = itm.defaultValue; + } + return this; // make chainable + } + setJSONLD(itm: any) { + itm.id = itm['@id']; + delete itm['@id']; + return this.set(itm); } get() { return { ...super.get(), - specializes: this.specializes, datatype: this.datatype, minCount: this.minCount, maxCount: this.maxCount, @@ -257,48 +277,65 @@ export class Property extends Identifiable implements IProperty { defaultValue: this.defaultValue }; } - validate() { - // if caller provided a itemType, ensure it matches expected - if (!this.itemType || this.itemType !== PigItemType.Property) - throw new Error(`Expected 'Property', but got ${this.itemType}`); - if (!Object.values(XsDataType).includes(this.datatype)) - throw new Error(`Invalid datatype: ${this.datatype}. Must be one of the XsDataType values.`); + getJSONLD() { + const { id, ...rest } = this.get(); + return { ['@id']: id, ...rest }; + /* const src = this.get(); + const itm = { ...src, ['@id']: src.id }; + delete itm.id; + return itm; */ + } + getHTML() { + return '
not implemented yet
'; + } + validate(itm: IProperty) { + if (!Object.values(XsDataType).includes(itm.datatype)) + throw new Error(`Invalid datatype: ${itm.datatype}. Must be one of the XsDataType values.`); // ToDo: implement further validation logic - return xhrOk; + return super.validate(itm); } } export interface IReference extends IIdentifiable { - specializes?: TPigId; // must be IRI of another Reference, translates to rdfs:subPropertyOf range: TPigId[]; // must be IRI of an Entity or Relationship (class) } export class Reference extends Identifiable implements IReference { - readonly specializes?: TPigId; - range: TPigId[]; - constructor(itm: IReference) { - super(itm); - this.specializes = itm.specializes; - this.range = itm.range; - this.validate(); + range!: TPigId[]; + constructor() { + super({ itemType: PigItemType.Reference }); } set(itm: IReference) { - super.set(itm); - // do not allow changing 'specializes' after creation - this.range = itm.range; - return this.validate(); + this.lastStatus = this.validate(itm); + if (this.lastStatus) { + super.set(itm); + this.range = itm.range; + } + return this; + } + setJSONLD(itm: any) { + itm.id = itm['@id']; + delete itm['@id']; + return this.set(itm); } get() { return { ...super.get(), - specializes: this.specializes, range: this.range }; } - validate() { - // if caller provided a itemType, ensure it matches expected - if (!this.itemType || this.itemType !== PigItemType.Reference) - throw new Error(`Expected 'Reference', but got ${this.itemType}`); + getJSONLD() { + const { id, ...rest } = this.get(); + return { ['@id']: id, ...rest }; + /* const src = this.get(); + const itm = { ...src, ['@id']: src.id }; + delete itm.id; + return itm; */ + } + getHTML() { + return '
not implemented yet
'; + } + validate(itm:IReference) { // ToDo: implement further validation logic - return xhrOk; + return super.validate(itm); } } @@ -309,17 +346,17 @@ export interface IEntity extends IElement { export class Entity extends Element implements IEntity { specializes?: TPigId; eligibleReference?: TPigId[]; - constructor(itm: IEntity) { - super(itm); - this.specializes = itm.specializes; - this.eligibleReference = itm.eligibleReference || []; - this.validate(); + constructor() { + super({ itemType: PigItemType.Entity }); } set(itm: IEntity) { - super.set(itm); - this.specializes = itm.specializes; - this.eligibleReference = itm.eligibleReference || []; - return this.validate(); + this.lastStatus = this.validate(itm); + if (this.lastStatus) { + super.set(itm); + this.specializes = itm.specializes; + this.eligibleReference = itm.eligibleReference || []; + } + return this; // make chainable } get() { return { @@ -328,11 +365,13 @@ export class Entity extends Element implements IEntity { eligibleReference: this.eligibleReference }; } - validate() { - if (!this.itemType || this.itemType !== PigItemType.Entity) - throw new Error(`Expected 'Entity', but got ${this.itemType}`); + validate(itm:IEntity) { + // if (!itm.itemType || itm.itemType !== PigItemType.Entity) + // throw new Error(`Expected '${PigItemType.Entity}', but got ${itm.itemType}`); + if (this.specializes && this.specializes !== itm.specializes) + throw new Error(`Cannot change the specialization of an Entity after creation (tried to change from ${this.specializes} to ${itm.specializes})`); // ToDo: implement further validation logic - return xhrOk; + return super.validate(itm); } } @@ -345,19 +384,18 @@ export class Relationship extends Element implements IRelationship { specializes?: TPigId; eligibleSource?: TPigId[]; eligibleTarget?: TPigId[]; - constructor(itm: IRelationship) { - super(itm); - this.specializes = itm.specializes; - this.eligibleSource = itm.eligibleSource || []; - this.eligibleTarget = itm.eligibleTarget || []; - this.validate(); + constructor() { + super({ itemType: PigItemType.Relationship }); } set(itm: IRelationship) { - super.set(itm); - this.specializes = itm.specializes; - this.eligibleSource = itm.eligibleSource || []; - this.eligibleTarget = itm.eligibleTarget || []; - return this.validate(); + this.lastStatus = this.validate(itm); + if (this.lastStatus) { + super.set(itm); + this.specializes = itm.specializes; + this.eligibleSource = itm.eligibleSource || []; + this.eligibleTarget = itm.eligibleTarget || []; + } + return this; } get() { return { @@ -367,39 +405,37 @@ export class Relationship extends Element implements IRelationship { eligibleTarget: this.eligibleTarget }; } - validate() { - if (!this.itemType || this.itemType !== PigItemType.Relationship) - throw new Error(`Expected 'Relationship', but got ${this.itemType}`); + validate(itm: IRelationship) { + // if (!itm.itemType || itm.itemType !== PigItemType.Relationship) + // throw new Error(`Expected '${PigItemType.Relationship}', but got ${itm.itemType}`); + if (this.specializes && this.specializes !== itm.specializes) + throw new Error(`Cannot change the specialization of a Relationship after creation (tried to change from ${this.specializes} to ${itm.specializes})`); // ToDo: implement further validation logic - return xhrOk; + return super.validate(itm); } } // For the instances/individuals, the 'payload': -export interface IAProperty { - itemType: PigItemTypeValue; +export interface IAProperty extends IItem { hasClass: TPigId; // must be IRI of an element of type Pig:Property, translates to @type resp. rdf:type + value?: string; aComposedProperty?: TPigId[]; - value: string; } -export class AProperty implements IAProperty { - readonly itemType!: PigItemTypeValue; +export class AProperty extends Item implements IAProperty { hasClass!: TPigId; + value?: string; aComposedProperty?: TPigId[]; - value: string; - constructor(itm: IAProperty) { - this.itemType = PigItemType.aProperty; - this.hasClass = itm.hasClass; - this.aComposedProperty = itm.aComposedProperty; - this.value = itm.value; - this.validate(); + constructor() { + super({ itemType: PigItemType.aProperty }); } set(itm: IAProperty) { - // itemType is readonly - this.hasClass = itm.hasClass; - this.aComposedProperty = itm.aComposedProperty; - this.value = itm.value; - return this.validate(); + this.lastStatus = this.validate(itm); + if (this.lastStatus) { + this.hasClass = itm.hasClass; + this.aComposedProperty = itm.aComposedProperty; + this.value = itm.value; + } + return this; } get() { return { @@ -413,36 +449,33 @@ export class AProperty implements IAProperty { // ToDo: implement a HTML snippet with the property value return ''; } - validate() { - if (!this.itemType || this.itemType !== PigItemType.aProperty) - throw new Error(`Expected 'aProperty', but got ${this.itemType}`); - if (!this.hasClass) - throw new Error(`Property instance must have a hasClass reference`); + validate(itm: IAProperty) { + // if (!this.itemType || this.itemType !== PigItemType.aProperty) + // throw new Error(`Expected 'aProperty', but got ${this.itemType}`); + if (!itm.hasClass) + throw new Error(`'${PigItemType.aProperty}' must have a hasClass reference`); // ToDo: implement further validation logic // - Check class reference; must be an existing Property IRI (requires access to the cache to resolve the class -> do it through overall consistency check): return xhrOk; } } -export interface IAReference { - itemType: PigItemTypeValue; +export interface IAReference extends IItem { hasClass: TPigId; // must be IRI of an element of type Pig:Reference, translates to @type resp. rdf:type element: TPigId; } -export class AReference implements IAReference { - readonly itemType!: PigItemTypeValue; +export class AReference extends Item implements IAReference { hasClass!: TPigId; - element: TPigId; - constructor(itm: IAReference) { - this.itemType = PigItemType.aReference; - this.hasClass = itm.hasClass; - this.element = itm.element; - this.validate(); + element!: TPigId; + constructor() { + super({ itemType: PigItemType.aReference }); } set(itm: IAReference) { - // itemType is readonly - this.hasClass = itm.hasClass; - this.element = itm.element; - return this.validate(); + this.lastStatus = this.validate(itm); + if (this.lastStatus) { + this.hasClass = itm.hasClass; + this.element = itm.element; + } + return this; } get() { return { @@ -455,11 +488,11 @@ export class AReference implements IAReference { // ToDo: implement a HTML snippet with a link to the referenced element return ''; } - validate() { - if (!this.itemType || this.itemType !== PigItemType.aReference) - throw new Error(`Expected 'aReference', but got ${this.itemType}`); - if (!this.hasClass) - throw new Error(`Reference instance must have a hasClass reference`); + validate(itm: IAReference) { + // if (!this.itemType || this.itemType !== PigItemType.aReference) + // throw new Error(`Expected 'aReference', but got ${this.itemType}`); + if (!itm.hasClass) + throw new Error(`'${PigItemType.aReference}' must have a hasClass reference`); // ToDo: implement further validation logic // - Check class reference; must be an existing Reference IRI (requires access to the cache to resolve the class -> do it through overall consistency check): return xhrOk; @@ -471,19 +504,19 @@ export interface IAnEntity extends IAnElement { hasReference?: IAReference[]; // optional, must hold anEntity or aRelationship IRIs } export class AnEntity extends AnElement implements IAnEntity { - hasClass: TPigId; - hasReference: AReference[]; - constructor(itm: IAnEntity) { - super(itm); - this.hasClass = itm.hasClass; - this.hasReference = itm.hasReference ? itm.hasReference.map(i => new AReference(i)) : []; - this.validate(); + hasClass!: TPigId; + hasReference!: AReference[]; + constructor() { + super({ itemType: PigItemType.anEntity }); } set(itm: IAnEntity) { - super.set(itm); - this.hasClass = itm.hasClass; - this.hasReference = itm.hasReference ? itm.hasReference.map(i => new AReference(i)) : []; - return this.validate(); + this.lastStatus = this.validate(itm); + if (this.lastStatus) { + super.set(itm); + this.hasClass = itm.hasClass; + this.hasReference = itm.hasReference ? itm.hasReference.map(i => new AReference().set(i)) : []; + } + return this; } get() { return { @@ -496,14 +529,14 @@ export class AnEntity extends AnElement implements IAnEntity { // ToDo: implement a HTML representation of the entity including its properties return ''; } - validate() { - if (!this.itemType || this.itemType !== PigItemType.anEntity) - throw new Error(`Expected 'anEntity', but got ${this.itemType}`); - if (!this.hasClass) - throw new Error(`'anEntity' must have a hasClass reference`); + validate(itm: IAnEntity) { + // if (!this.itemType || this.itemType !== PigItemType.anEntity) + // throw new Error(`Expected 'anEntity', but got ${this.itemType}`); + if (!itm.hasClass) + throw new Error(`'${PigItemType.anEntity}' must have a hasClass reference`); // ToDo: implement further validation logic // - Check class reference; must be an existing Entity IRI (requires access to the cache to resolve the class -> do it through overall consistency check): - return xhrOk; + return super.validate(itm); } } @@ -513,22 +546,21 @@ export interface IARelationship extends IAnElement { hasTarget: IAReference; } export class ARelationship extends AnElement implements IARelationship { - hasClass: TPigId; + hasClass!: TPigId; hasSource!: AReference; hasTarget!: AReference; - constructor(itm: IARelationship) { - super(itm); - this.hasClass = itm.hasClass; - this.hasSource = new AReference(itm.hasSource); - this.hasTarget = new AReference(itm.hasTarget); - this.validate(); + constructor() { + super({ itemType: PigItemType.aRelationship }); } set(itm: IARelationship) { - super.set(itm); - this.hasClass = itm.hasClass; - this.hasSource = new AReference(itm.hasSource); - this.hasTarget = new AReference(itm.hasTarget); - return this.validate(); + this.lastStatus = this.validate(itm); + if (this.lastStatus) { + super.set(itm); + this.hasClass = itm.hasClass; + this.hasSource = new AReference().set(itm.hasSource); + this.hasTarget = new AReference().set(itm.hasTarget); + } + return this; } get() { return { @@ -542,14 +574,14 @@ export class ARelationship extends AnElement implements IARelationship { // ToDo: implement a HTML representation of the relationship including its properties return ''; } - validate() { - if (!this.itemType || this.itemType !== PigItemType.aRelationship) - throw new Error(`Expected 'aRelationship', but got ${this.itemType}`); - if (!this.hasClass) - throw new Error(`'aRelationship' must have a hasClass reference`); + validate(itm: IARelationship) { + // if (!this.itemType || this.itemType !== PigItemType.aRelationship) + // throw new Error(`Expected 'aRelationship', but got ${this.itemType}`); + if (!itm.hasClass) + throw new Error(`'${PigItemType.aRelationship}' must have a hasClass reference`); // ToDo: implement further validation logic // - Check class reference; must be an existing Relationship IRI (requires access to the cache to resolve the class -> do it through overall consistency check): - return xhrOk; + return super.validate(itm); } } diff --git a/tests/data/JSON-LD/Project 'Very Simple Model (FMC) with Requirements'.pig.jsonld b/tests/data/JSON-LD/Project 'Very Simple Model (FMC) with Requirements'.pig.jsonld new file mode 100644 index 0000000..73b475e --- /dev/null +++ b/tests/data/JSON-LD/Project 'Very Simple Model (FMC) with Requirements'.pig.jsonld @@ -0,0 +1 @@ +{"@context":{"o":"https://product-information-graph.org/v0.2/ontology#","d":"https://product-information-graph.org/examples/09_Very-Simple-Model-FMC-with-Requirements.specif.zip#","rdf":"http://www.w3.org/1999/02/22-rdf-syntax-ns#","rdfs":"http://www.w3.org/2000/01/rdf-schema#","owl":"http://www.w3.org/2002/07/owl#","sh":"http://www.w3.org/ns/shacl#","W3C":"https://www.w3.org/standards/semanticweb#","xs":"http://www.w3.org/2001/XMLSchema#","xsd":"http://www.w3.org/2001/XMLSchema#","dcterms":"http://purl.org/dc/terms/","dc":"http://purl.org/dc/elements/1.1/","schema":"https://schema.org#","iana":"https://www.iana.org/assignments/media-types#","foaf":"http://xmlns.com/foaf/spec/#","FMC":"http://fmc-modeling.org#","RFLP":"https://product-information-graph.org/v0.2/ontology/RFLP#","IR":"https://www.adesso.de/de/impulse/interaction-room/index.jsp#","IREB":"https://cpre.ireb.org/en/downloads-and-resources/glossary#","ReqIF":"https://www.prostep.org/fileadmin/downloads/PSI_ImplementationGuide_ReqIF_V1-7.pdf#","ReqIF-WF":"https://www.prostep.org/fileadmin/downloads/Recommendation_ReqIF_V2_RZ3.pdf#","HIS":"https://www.itwissen.info/Herstellerinitiative-Software-Automotive-HIS.html#","oslc":"http://open-services.net/ns/core#","oslc_rm":"http://open-services.net/ns/rm#","oslc_cm":"http://open-services.net/ns/cm#","ArchiMate":"https://pubs.opengroup.org#","bpmn":"https://www.bpmn.org#","uml":"https://www.omg.org/spec/UML#","sysml":"https://www.omg.org/spec/SysML#","pig":"https://product-information-graph.org/v0.2/metamodel#","DDP":"http://www.prostep.org/dictionary/2.0#","SpecIF":"https://specif.de/v1.2/schema#"},"@id":"d:ACP-Very-Simple-Model-FMC-with-Requirements","@type":"pig:Package","dcterms:title":[{"@value":"Project 'Very Simple Model (FMC) with Requirements'"}],"dcterms:modified":"2025-12-05T17:56:04.852Z","@graph":[{"@id":"pig:Entity","@type":"owl:Class","pig:itemType":{"@id":"pig:Entity"},"dcterms:title":"Entity","dcterms:description":"A PIG meta-model element used for entities (aka resources or artifacts).","pig:eligibleProperty":[{"@id":"rdfs:label"},{"@id":"rdfs:comment"},{"@id":"pig:category"},{"@id":"pig:icon"}]},{"@id":"pig:Organizer","pig:specializes":{"@id":"pig:Entity"},"pig:itemType":{"@id":"pig:Entity"},"dcterms:title":"Organizer","dcterms:description":"An element organizing model elements. An example is a list of requirements or a diagram using a certain notation.","pig:eligibleProperty":[{"@id":"rdfs:label"},{"@id":"rdfs:comment"},{"@id":"pig:category"}]},{"@id":"pig:HierarchyRoot","pig:specializes":{"@id":"pig:Organizer"},"pig:itemType":{"@id":"pig:Entity"},"dcterms:title":"Hierarchy Root","dcterms:description":"A subclass of PIG organizer serving as a root for hierarchically organized graph elements.","pig:eligibleProperty":[{"@id":"rdfs:label"},{"@id":"rdfs:comment"}],"pig:eligibleReference":[{"@id":"pig:lists"}]},{"@id":"pig:Outline","pig:specializes":{"@id":"pig:Organizer"},"pig:itemType":{"@id":"pig:Entity"},"dcterms:title":"Outline","dcterms:description":"A subclass of PIG organizer comprising all information items of a human-readable document. As usual, the outline is hierarchically organized.","pig:eligibleProperty":[{"@id":"rdfs:label"},{"@id":"rdfs:comment"},{"@id":"pig:category"}],"pig:eligibleReference":[{"@id":"pig:lists"}]},{"@id":"pig:View","pig:specializes":{"@id":"pig:Organizer"},"pig:itemType":{"@id":"pig:Entity"},"dcterms:title":"View","dcterms:description":"A subclass of PIG organizer representing a model view (diagram) using a certain notation showing selected model elements.","pig:eligibleProperty":[{"@id":"rdfs:label"},{"@id":"rdfs:comment"},{"@id":"pig:category"},{"@id":"pig:icon"}],"pig:eligibleReference":[{"@id":"pig:shows"},{"@id":"pig:depicts"}]},{"@id":"pig:Relationship","@type":"owl:Class","pig:itemType":{"@id":"pig:Relationship"},"dcterms:title":"Relationship","dcterms:description":"A PIG meta-model element used for reified relationships (aka predicates).","pig:eligibleProperty":[{"@id":"rdfs:label"},{"@id":"rdfs:comment"},{"@id":"pig:category"}]},{"@id":"pig:icon","@type":"owl:DatatypeProperty","pig:itemType":{"@id":"pig:Property"},"dcterms:title":"has icon","dcterms:description":"Specifies an icon for a model element (entity or relationship).","sh:datatype":{"@id":"xs:string"},"sh:minCount":0,"sh:maxCount":1},{"@id":"pig:category","pig:specializes":{"@id":"dcterms:type"},"pig:itemType":{"@id":"pig:Property"},"dcterms:title":"has category","dcterms:description":"Specifies a category for an element (entity, relationship or organizer).","sh:datatype":{"@id":"xs:string"},"sh:maxLength":32,"sh:minCount":0,"sh:maxCount":1},{"@id":"pig:eligibleSource","@type":"owl:ObjectProperty","pig:itemType":{"@id":"pig:Reference"},"rdfs:range":[{"@id":"pig:Entity"},{"@id":"pig:Relationship"}],"dcterms:title":"has source","dcterms:description":"Connects the source of a reified relationship."},{"@id":"pig:eligibleTarget","@type":"owl:ObjectProperty","pig:itemType":{"@id":"pig:Reference"},"rdfs:range":[{"@id":"pig:Entity"},{"@id":"pig:Relationship"}],"dcterms:title":"has target","dcterms:description":"Connects the target of a reified relationship."},{"@id":"pig:eligibleReference","@type":"owl:ObjectProperty","pig:itemType":{"@id":"pig:Reference"},"rdfs:range":[{"@id":"pig:Entity"},{"@id":"pig:Relationship"},{"@id":"pig:Organizer"}],"dcterms:title":"references","dcterms:description":"References an entity, a relationship or a subordinated organizer."},{"@id":"pig:lists","pig:specializes":{"@id":"pig:eligibleReference"},"pig:itemType":{"@id":"pig:Reference"},"rdfs:range":[{"@id":"pig:Entity"},{"@id":"pig:Relationship"},{"@id":"pig:Organizer"}],"dcterms:title":"lists","dcterms:description":"Lists an entity, a relationship or a subordinated organizer."},{"@id":"pig:shows","pig:specializes":{"@id":"pig:eligibleReference"},"pig:itemType":{"@id":"pig:Reference"},"rdfs:range":[{"@id":"pig:Entity"},{"@id":"pig:Relationship"}],"dcterms:title":"shows","dcterms:description":"Shows an entity or a relationship."},{"@id":"pig:depicts","pig:specializes":{"@id":"pig:eligibleReference"},"pig:itemType":{"@id":"pig:Reference"},"rdfs:range":[{"@id":"pig:Entity"}],"dcterms:title":"depicts","dcterms:description":"Depicts an entity; inverse of uml:ownedDiagram."},{"@id":"dcterms:title","dcterms:title":[{"@value":"Title","@language":"en"},{"@value":"Titel","@language":"de"},{"@value":"Titre","@language":"fr"}],"dcterms:description":[{"@value":"

A name given to the resource. (source: DCMI)

Title (reference: Dublin Core) of the resource represented as rich text in XHTML content. SHOULD include only content that is valid inside an XHTML 'span' element. (source: OSLC)

","@language":"en"}],"@type":"owl:DatatypeProperty","pig:itemType":{"@id":"pig:Property"},"sh:datatype":{"@id":"xsd:string"},"sh:maxCount":"1","sh:maxLength":"256"},{"@id":"dcterms:description","dcterms:title":[{"@value":"Description","@language":"en"},{"@value":"Beschreibung","@language":"de"},{"@value":"Description","@language":"fr"}],"dcterms:description":[{"@value":"

An account of the resource. (source: DCMI)

Descriptive text (reference: Dublin Core) about resource represented as rich text in XHTML content. SHOULD include only content that is valid and suitable inside an XHTML 'div' element. (source: OSLC)

","@language":"en"}],"@type":"owl:DatatypeProperty","pig:itemType":{"@id":"pig:Property"},"sh:datatype":{"@id":"xsd:string"},"sh:maxCount":"1"},{"@id":"SpecIF:Diagram","dcterms:title":[{"@value":"Diagram","@language":"en"},{"@value":"Diagramm","@language":"de"},{"@value":"Diagramme","@language":"fr"}],"dcterms:description":[{"@value":"A diagram illustrating the resource or a link to a diagram.","@language":"en"}],"@type":"owl:DatatypeProperty","pig:itemType":{"@id":"pig:Property"},"sh:datatype":{"@id":"xsd:string"}},{"@id":"SpecIF:Priority","dcterms:title":[{"@value":"Priority","@language":"en"},{"@value":"Priorität","@language":"de"},{"@value":"Priorité","@language":"fr"}],"dcterms:description":[{"@value":"Enumerated values for the 'Priority' of the resource.","@language":"en"}],"@type":"owl:ObjectProperty","pig:itemType":{"@id":"pig:Property"},"pig:eligibleValue":[{"@id":"SpecIF:priorityHigh","dcterms:title":[{"@value":"high","@language":"en"},{"@value":"hoch","@language":"de"},{"@value":"haut","@language":"fr"}]},{"@id":"SpecIF:priorityMedium","dcterms:title":[{"@value":"medium","@language":"en"},{"@value":"mittel","@language":"de"},{"@value":"moyen","@language":"fr"}]},{"@id":"SpecIF:priorityLow","dcterms:title":[{"@value":"low","@language":"en"},{"@value":"niedrig","@language":"de"},{"@value":"bas","@language":"fr"}]}]},{"@id":"SpecIF:Paragraph","dcterms:title":[{"@value":"Paragraph","@language":"en"},{"@value":"Textabsatz","@language":"de"},{"@value":"Paragraphe","@language":"fr"}],"dcterms:description":[{"@value":"

A 'Paragraph' is an unspecified information in a document at any level.

","@language":"en"},{"@value":"

Ein 'Textabschnitt' in einem Dokument auf beliebiger Ebene.

","@language":"de"}],"pig:specializes":{"@id":"pig:Entity"},"pig:eligibleProperty":[{"@id":"dcterms:title"},{"@id":"dcterms:description"},{"@id":"SpecIF:Diagram"},{"@id":"pig:category"}],"pig:itemType":{"@id":"pig:Entity"}},{"@id":"FMC:Actor","dcterms:title":[{"@value":"Actor","@language":"en"},{"@value":"Akteur","@language":"de"},{"@value":"Acteur","@language":"fr"}],"dcterms:description":[{"@value":"

An 'Actor' is a fundamental model element type representing an active entity, be it an activity, a process step, a function, a system component or a role.

The particular use or original type is specified with a [[dcterms:type]] property of the 'FMC:Actor'. A value of that property should be an ontology-term, such as [[bpmn:processStep]].

","@language":"en"},{"@value":"

Ein 'Akteur' ist ein fundamentaler Modellelementtyp, der eine aktive Entität darstellt, sei es eine Aktivität, ein Prozessschritt, eine Funktion, eine Systemkomponente oder eine Rolle.

Die spezielle Verwendung oder der ursprüngliche Typ wird mit einer [[dcterms:type]] Eigenschaft von 'FMC:Actor' spezifiziert. Die Werte dieser Eigenschaft können Ontologiebegriffe sein, wie z.B. [[bpmn:timer]].

","@language":"de"},{"@value":"

Un 'Acteur' est un type d'élément de modèle fondamental représentant une entité active, qu'il s'agisse d'une activité, d'une étape de processus, d'une fonction, d'un composant de système ou d'un rôle.

L'utilisation particulière ou le type original est spécifié avec une propriété [[dcterms:type]] de 'FMC:Actor'. Les valeurs de cette propriété peuvent être des termes d'ontologie, tels que [[bpmn:timer]].

","@language":"fr"}],"pig:specializes":{"@id":"pig:Entity"},"pig:icon":"□","pig:eligibleProperty":[{"@id":"dcterms:title"},{"@id":"dcterms:description"},{"@id":"pig:category"}],"pig:itemType":{"@id":"pig:Entity"}},{"@id":"FMC:State","dcterms:title":[{"@value":"State","@language":"en"},{"@value":"Zustand","@language":"de"},{"@value":"État","@language":"fr"}],"dcterms:description":[{"@value":"

A 'State' is a fundamental model element type representing a passive entity, be it a value, a condition, an information storage or even a physical shape.

The particular use or the original type is specified with a [[dcterms:type]] property of the 'FMC:State'. A value of that property should bean ontology-term, such as [[bpmn:dataObject]].

","@language":"en"},{"@value":"

Ein 'Zustand' ist ein fundamentaler Modellelementtyp, der eine passive Entität darstellt, sei es ein Wert, ein Dokument, ein Informationsspeicher, eine Bedingung oder eine physische Beschaffenheit.

Die spezielle Verwendung oder der ursprüngliche Typ wird mit einer [[dcterms:type]] Eigenschaft von 'FMC:State' spezifiziert. Die Werte dieser Eigenschaft können Ontologiebegriffe sein, wie z.B. [[ArchiMate:DataObject]].

","@language":"de"},{"@value":"

Un 'État' est un type d'élément de modèle fondamental représentant une entité passive, qu'il s'agisse d'une valeur, d'une condition, d'un stockage d'informations ou même d'une forme physique.

L'utilisation particulière ou le type original est spécifié avec une propriété [[dcterms:type]] de 'FMC:State'. Les valeurs de cette propriété peuvent être des termes d'ontologie, tels que [[ArchiMate:DataObject]].

","@language":"fr"}],"pig:specializes":{"@id":"pig:Entity"},"pig:icon":"○","pig:eligibleProperty":[{"@id":"dcterms:title"},{"@id":"dcterms:description"},{"@id":"pig:category"}],"pig:itemType":{"@id":"pig:Entity"}},{"@id":"FMC:Event","dcterms:title":[{"@value":"Event","@language":"en"},{"@value":"Ereignis","@language":"de"},{"@value":"Évenement","@language":"fr"}],"dcterms:description":[{"@value":"

An 'Event' is a fundamental model element type representing a time reference, a change in condition/value or more generally a synchronization primitive.

The particular use or the original type is specified with a [[dcterms:type]] property of the 'FMC:Event'. A value of that property should be an ontology-term, such as [[bpmn:startEvent]].

","@language":"en"},{"@value":"

Ein 'Ereignis' ist ein fundamentaler Modellelementtyp, der eine Zeitreferenz, eine Änderung einer Bedingung/eines Wertes oder allgemeiner ein Synchronisationsmittel darstellt.

Die spezielle Verwendung oder der ursprüngliche Typ wird mit einer [[dcterms:type]] Eigenschaft von 'FMC:Event' spezifiziert. Die Werte dieser Eigenschaft sollen Ontologiebegriffe sein, wie z.B. [[bpmn:startEvent]].

","@language":"de"},{"@value":"

Un 'Événement' est un type d'élément de modèle fondamental représentant une référence temporelle, un changement de condition/valeur ou plus généralement une primitive de synchronisation.

L'utilisation particulière ou le type original est spécifié avec une propriété [[dcterms:type]] de 'FMC:Event'. Les valeurs de cette propriété peuvent être des termes d'ontologie, tels que [[bpmn:startEvent]].

","@language":"fr"}],"pig:specializes":{"@id":"pig:Entity"},"pig:icon":"♢","pig:eligibleProperty":[{"@id":"dcterms:title"},{"@id":"dcterms:description"},{"@id":"pig:category"}],"pig:itemType":{"@id":"pig:Entity"}},{"@id":"IREB:Requirement","dcterms:title":[{"@value":"Requirement","@language":"en"},{"@value":"Anforderung","@language":"de"},{"@value":"Exigence","@language":"fr"}],"dcterms:description":[{"@value":"

A 'Requirement' is a singular documented physical and functional need that a particular design, product or process must be able to perform. (source: Wikipedia)

Definition:

  1. A condition or capability needed by a user to solve a problem or achieve an objective.
  2. A condition or capability that must be met or possessed by a system or system component to satisfy a contract, standard, specification, or other formally imposed documents.
  3. A documented representation of a condition or capability as in (1) or (2).

Note: The definition above is the classic one from IEEE Std 610.12 of 1990. Alternatively, we also give a more modern definition:

  1. A need perceived by a stakeholder.
  2. A capability or property that a system shall have.
  3. A documented representation of a need, capability or property.
","@language":"en"},{"@value":"

Eine 'Anforderung' ist ein einzelnes dokumentiertes physisches und funktionales Bedürfnis, das ein bestimmter Entwurf, ein Produkt oder ein Prozess erfüllen muss. (source: Wikipedia)

Definition:

  1. Eine Bedingung oder Fähigkeit, die ein Benutzer benötigt, um ein Problem zu lösen oder ein Ziel zu erreichen.
  2. Eine Bedingung oder Fähigkeit, die ein System oder eine Systemkomponente erfüllen oder besitzen muss, um einen Vertrag, eine Norm, eine Spezifikation oder ein anderes formal vorgeschriebenes Dokument zu erfüllen.
  3. Eine dokumentierte Darstellung einer Bedingung oder Fähigkeit wie in (1) oder (2).

Anmerkung: Die obige Definition ist die klassische Definition aus IEEE Std 610.12 von 1990. Alternativ geben wir auch eine modernere Definition an:

  1. Ein von einem Stakeholder wahrgenommener Bedarf.
  2. Eine Fähigkeit oder Eigenschaft, die ein System haben soll.
  3. Eine dokumentierte Darstellung eines Bedarfs, einer Fähigkeit oder Eigenschaft.
","@language":"de"},{"@value":"

Une 'Exigence' est un besoin physique et fonctionnel unique et documenté qu'une conception, un produit ou un processus particulier doit pouvoir satisfaire. (source: Wikipedia)

Définition:

  1. Condition ou capacité dont un utilisateur a besoin pour résoudre un problème ou atteindre un objectif.
  2. Condition ou capacité qui doit être remplie ou possédée par un système ou un composant de système pour satisfaire à un contrat, à une norme, à une spécification ou à d'autres documents imposés officiellement.
  3. Une représentation documentée d'une condition ou d'une capacité comme dans (1) ou (2).

Remarque: La définition ci-dessus est la définition classique de la norme IEEE 610.12 de 1990. Nous donnons également une définition plus moderne:

  1. Un besoin perçu par une partie prenante;
  2. Une capacité ou une propriété qu'un système doit avoir.
  3. Une représentation documentée d'un besoin, d'une capacité ou d'une propriété.
","@language":"fr"}],"pig:specializes":{"@id":"pig:Entity"},"pig:icon":"↯","pig:eligibleProperty":[{"@id":"dcterms:title"},{"@id":"dcterms:description"},{"@id":"SpecIF:Priority"}],"pig:itemType":{"@id":"pig:Entity"}},{"@id":"SpecIF:writes","dcterms:title":[{"@value":"writes","@language":"en"},{"@value":"schreibt","@language":"de"},{"@value":"écrit","@language":"fr"}],"dcterms:description":[{"@value":"A [[FMC:Actor]] 'writes' (changes) a [[FMC:State]].","@language":"en"}],"pig:specializes":{"@id":"pig:Relationship"},"pig:eligibleProperty":[{"@id":"dcterms:description"}],"pig:itemType":{"@id":"pig:Relationship"},"pig:eligibleSource":[{"@id":"FMC:Actor"}],"pig:eligibleTarget":[{"@id":"FMC:State"}]},{"@id":"SpecIF:reads","dcterms:title":[{"@value":"reads","@language":"en"},{"@value":"liest","@language":"de"},{"@value":"lit","@language":"fr"}],"dcterms:description":[{"@value":"A [[FMC:Actor]] 'reads' a [[FMC:State]].","@language":"en"}],"pig:specializes":{"@id":"pig:Relationship"},"pig:eligibleProperty":[{"@id":"dcterms:description"}],"pig:itemType":{"@id":"pig:Relationship"},"pig:eligibleSource":[{"@id":"FMC:Actor"}],"pig:eligibleTarget":[{"@id":"FMC:State"}]},{"@id":"oslc_rm:satisfies","dcterms:title":[{"@value":"satisfies","@language":"en"},{"@value":"erfüllt","@language":"de"},{"@value":"satisfait","@language":"fr"}],"dcterms:description":[{"@value":"

The object is satisfied by the subject. (source: OSLC)

SpecIF suggests that the subject is confined to a model element, e.g, a [[FMC:Actor]] or [[FMC:State]], and the object is confined to a [[IREB:Requirement]]. More concretely, an example for this type of statement is 'Component-X satisfies 'Requirement-4711'.

","@language":"en"}],"pig:specializes":{"@id":"pig:Relationship"},"pig:eligibleProperty":[{"@id":"dcterms:description"}],"pig:itemType":{"@id":"pig:Relationship"},"pig:eligibleSource":[{"@id":"FMC:Actor"},{"@id":"FMC:State"}],"pig:eligibleTarget":[{"@id":"IREB:Requirement"}]},{"@id":"d:Req-1a8016e2872e78ecadc50feddc00029b","@type":"IREB:Requirement","dcterms:modified":"2020-10-17T10:00:00+01:00","dcterms:title":[{"@value":"Data Volume"}],"dcterms:description":[{"@value":"

The data store MUST support a total volume up to 850 GB.

"}],"SpecIF:Priority":[{"@id":"SpecIF:priorityHigh"}],"pig:itemType":{"@id":"pig:anEntity"}},{"@id":"d:Req-0Z7916e2872e78ecadc50feddc00918a","@type":"IREB:Requirement","dcterms:modified":"2020-10-17T10:00:00+01:00","dcterms:title":[{"@value":"Consistency"}],"dcterms:description":[{"@value":"

The data store MUST be consistent at all times.

"}],"SpecIF:Priority":[{"@id":"SpecIF:priorityHigh"}],"pig:itemType":{"@id":"pig:anEntity"}},{"@id":"d:Req-2b9016e2872e78ecadc50feddc0013Ac","@type":"IREB:Requirement","dcterms:modified":"2020-10-17T10:00:00+01:00","dcterms:title":[{"@value":"Response Time"}],"dcterms:description":[{"@value":"

The system SHOULD respond on user queries within 300 ms.

"}],"SpecIF:Priority":[{"@id":"SpecIF:priorityMedium"}],"pig:itemType":{"@id":"pig:anEntity"}},{"@id":"d:Diagram-aec0df7900010000017001eaf53e8876","@type":"pig:View","dcterms:modified":"2020-03-06T08:32:00+01:00","dcterms:title":[{"@value":"IT-Integration: FiCo-Application and FiCo-Data"}],"SpecIF:Diagram":[{"@value":"

Model Diagram:

Notation: FMC Block Diagram

"}],"pig:category":[{"@value":"FMC Block Diagram"}],"pig:itemType":{"@id":"pig:anEntity"},"pig:shows":[{"@id":"d:MEl-50fbfe8f0029b1a8016ea86245a9d83a"},{"@id":"d:MEl-50feddc00029b1a8016e2872e78ecadc"},{"@id":"d:SWri-50fbfe8f0029b1a8016ea86245a9d83a-50feddc00029b1a8016e2872e78ecadc"},{"@id":"d:SRea-50fbfe8f0029b1a8016ea86245a9d83a-50feddc00029b1a8016e2872e78ecadc"}]},{"@id":"d:MEl-50fbfe8f0029b1a8016ea86245a9d83a","@type":"FMC:Actor","dcterms:modified":"2020-03-06T09:04:00+01:00","dcterms:title":[{"@value":"FiCo-Application"}],"dcterms:description":[{"@value":"

IT-Application for Finance and Controlling.

"}],"pig:itemType":{"@id":"pig:anEntity"}},{"@id":"d:MEl-50feddc00029b1a8016e2872e78ecadc","@type":"FMC:State","dcterms:modified":"2020-03-06T09:03:00+01:00","dcterms:title":[{"@value":"FiCo-Data"}],"dcterms:description":[{"@value":"

Finance and Controlling Data, such as cost-units per project with budget, accrued cost etc.

"}],"pig:itemType":{"@id":"pig:anEntity"}},{"@id":"d:SWri-50fbfe8f0029b1a8016ea86245a9d83a-50feddc00029b1a8016e2872e78ecadc","@type":"SpecIF:writes","dcterms:modified":"2020-03-06T09:05:00+01:00","dcterms:description":[{"@value":"'FiCo-Application' writes 'FiCo-Data'"}],"pig:itemType":{"@id":"pig:aRelationship"},"pig:hasSource":{"@id":"d:MEl-50fbfe8f0029b1a8016ea86245a9d83a"},"pig:hasTarget":{"@id":"d:MEl-50feddc00029b1a8016e2872e78ecadc"}},{"@id":"d:SRea-50fbfe8f0029b1a8016ea86245a9d83a-50feddc00029b1a8016e2872e78ecadc","@type":"SpecIF:reads","dcterms:modified":"2020-03-06T09:05:00+01:00","dcterms:description":[{"@value":"'FiCo-Application' reads 'FiCo-Data'"}],"pig:itemType":{"@id":"pig:aRelationship"},"pig:hasSource":{"@id":"d:MEl-50fbfe8f0029b1a8016ea86245a9d83a"},"pig:hasTarget":{"@id":"d:MEl-50feddc00029b1a8016e2872e78ecadc"}},{"@id":"d:Ssat-50feddc00029b1a8016e2872e78ecadc-1a8016e2872e78ecadc50feddc00029b","@type":"oslc_rm:satisfies","dcterms:modified":"2020-10-17T10:00:00+01:00","dcterms:description":[{"@value":"'FiCo-Data' satisfies 'Data Volume'"}],"pig:itemType":{"@id":"pig:aRelationship"},"pig:hasSource":{"@id":"d:MEl-50feddc00029b1a8016e2872e78ecadc"},"pig:hasTarget":{"@id":"d:Req-1a8016e2872e78ecadc50feddc00029b"}},{"@id":"d:Ssat-50feddc00029b1a8016e2872e78ecadc-0Z7916e2872e78ecadc50feddc00918a","@type":"oslc_rm:satisfies","dcterms:modified":"2020-10-17T10:00:00+01:00","dcterms:description":[{"@value":"'FiCo-Data' satisfies 'Consistency'"}],"pig:itemType":{"@id":"pig:aRelationship"},"pig:hasSource":{"@id":"d:MEl-50feddc00029b1a8016e2872e78ecadc"},"pig:hasTarget":{"@id":"d:Req-0Z7916e2872e78ecadc50feddc00918a"}},{"@id":"d:Ssat-50fbfe8f0029b1a8016ea86245a9d83a-2b9016e2872e78ecadc50feddc0013Ac","@type":"oslc_rm:satisfies","dcterms:modified":"2020-10-17T10:00:00+01:00","dcterms:description":[{"@value":"'FiCo-Application' satisfies 'Response Time'"}],"pig:itemType":{"@id":"pig:aRelationship"},"pig:hasSource":{"@id":"d:MEl-50fbfe8f0029b1a8016ea86245a9d83a"},"pig:hasTarget":{"@id":"d:Req-2b9016e2872e78ecadc50feddc0013Ac"}},{"@id":"d:HierarchyRoot","@type":"pig:HierarchyRoot","dcterms:title":"Hierarchy Root","dcterms:description":"... anchoring all hierarchies of this graph (package)","pig:lists":[{"@id":"d:O-ACP-Very-Simple-Model-FMC-with-Requirements"}]},{"@id":"d:Folder-Introduction","@type":"pig:Outline","dcterms:modified":"2025-02-07T08:32:00+01:00","dcterms:title":[{"@value":"Introduction"}],"dcterms:description":[{"@value":"

This is a minimal showcase for a model with model-elements and related requirements. It covers pretty much all characteristics neeeded in such cases (including 'statements on statements'), so that transformations and expressive power of other data formats can be evaluated. The example and its representation in SpecIF format is discussed in Tutorial 6: Very Simple Model (FMC) and Tutorial 9: Very Simple Model (FMC) with Requirements.

"}]},{"@id":"d:Folder-Requirements","@type":"pig:Outline","dcterms:modified":"2020-03-06T08:32:00+01:00","dcterms:title":[{"@value":"Requirements"}],"pig:lists":[{"@id":"d:Req-1a8016e2872e78ecadc50feddc00029b"},{"@id":"d:Req-0Z7916e2872e78ecadc50feddc00918a"},{"@id":"d:Req-2b9016e2872e78ecadc50feddc0013Ac"}]},{"@id":"d:Folder-SystemModel","@type":"pig:Outline","dcterms:modified":"2020-03-06T08:32:00+01:00","dcterms:title":[{"@value":"System Model"}],"pig:lists":[{"@id":"d:Diagram-aec0df7900010000017001eaf53e8876"}]},{"@id":"d:FolderGlossary-10875487071","@type":"pig:Outline","dcterms:modified":"2025-12-05T17:55:54.531Z","dcterms:title":[{"@value":"Model Elements (Glossary)"}],"pig:category":[{"@id":"SpecIF:Glossary"}],"pig:lists":[{"@id":"d:MEl-50fbfe8f0029b1a8016ea86245a9d83a"},{"@id":"d:MEl-50feddc00029b1a8016e2872e78ecadc"}]},{"@id":"d:O-ACP-Very-Simple-Model-FMC-with-Requirements","@type":"pig:Outline","dcterms:modified":"2025-01-26T17:06:19.300Z","dcterms:title":[{"@value":"Project 'Very Simple Model (FMC) with Requirements'"}],"pig:category":[{"@id":"SpecIF:Heading"}],"pig:lists":[{"@id":"d:Folder-Introduction"},{"@id":"d:Folder-Requirements"},{"@id":"d:Folder-SystemModel"},{"@id":"d:FolderGlossary-10875487071"}]}]} \ No newline at end of file diff --git a/tests/data/JSON-LD/files_and_images/Very-Simple-Model-FMC.svg b/tests/data/JSON-LD/files_and_images/Very-Simple-Model-FMC.svg new file mode 100644 index 0000000..da9fc35 --- /dev/null +++ b/tests/data/JSON-LD/files_and_images/Very-Simple-Model-FMC.svg @@ -0,0 +1,54 @@ + + + + Notation: FMC Block Diagramm, fmc.bd, aec0df7900010000017001eaf53e8876 + + FiCo-Data, fmc.bd.storage, aec10382ffe793790170ac81c49b15f1 + + null, figure.plane, null + + + + + + + + + null, supplement, null + FiCo-Data + + + + + FiCo-Application, fmc.bd.agent, aec19cbaffe79379017062c66543f984 + + null, figure.plane, null + + + + + + + null, supplement, null + FiCo-Application + + + + + , fmc.bd.modifyingstorageaccess, aec24748ffe793790170938a7b8327e0 + + null, figure.lineshape, null + + + null, figure.lineshape, null + + + + + null, figure.lineshape, null + + + + + + diff --git a/tests/unit/pig-metaclasses.spec.ts b/tests/unit/pig-metaclasses.spec.ts index 064381e..e5c969a 100644 --- a/tests/unit/pig-metaclasses.spec.ts +++ b/tests/unit/pig-metaclasses.spec.ts @@ -15,6 +15,7 @@ import { Property, AProperty, Entity, AnEntity, Relationship, ARelationship } fr describe("PIG Metaclasses", () => { let propertyClass_input: IProperty; + let propertyClass_input_JSONLD: any; let property_input: IAProperty; let entityClass_input: IEntity; let entity1_input: IAnEntity; @@ -28,90 +29,99 @@ describe("PIG Metaclasses", () => { propertyClass_input = { id: "dcterms:type", itemType: PigItemType.Property, - title: {text:"The type or category", lang:"en"}, - description: {text: "This is a class for a property named dcterms:type for use by anEntity or aRelationship", lang: "en" }, + title: { text: "The type or category", lang: "en" }, + description: { text: "This is a class for a property named dcterms:type for use by anEntity or aRelationship", lang: "en" }, datatype: XsDataType.String, minCount: 0, maxCount: 1, maxLength: 20, defaultValue: "default_category" - } + }; + propertyClass_input_JSONLD = Object.assign( + {}, + propertyClass_input, + { + ['@id']: "dcterms:type", + id: undefined + } + ); property_input = { - // itemType: PigItemType.aProperty, + itemType: PigItemType.aProperty, hasClass: "dcterms:type", value: "A category" // usually a property belongs to a certain entity or relationship } // Entity with class: entityClass_input = { - id: "o:entityClass_1", + id: "o:Entity_1", itemType: PigItemType.Entity, title: { text: "Title of Entity Class 1", lang: "en" }, - description: { text: "Description of o:entityClass_1", lang: "en" }, + description: { text: "Description of o:Entity_1", lang: "en" }, + eligibleReference: [], eligibleProperty: ["dcterms:type"] }; entity1_input = { id: "d:anEntity_1", revision: "v1.0", - itemType: PigItemType.Entity, + itemType: PigItemType.anEntity, modified: new Date(), creator: "test_user", - title: { text: "Title of Entity 1", lang: "en" }, - description: { text: "Description of d:entity_1", lang: "en" }, + title: { text: "Title of anEntity 1", lang: "en" }, + description: { text: "Description of d:anEntity_1", lang: "en" }, - hasClass: "o:entityClass_1", + hasClass: "o:Entity_1", hasProperty: [{ - // itemType: PigItemType.aProperty, + itemType: PigItemType.aProperty, hasClass: "dcterms:type", - value: "Category of Entity_1" + value: "Category of anEntity_1" }] }; entity2_input = { id: "d:anEntity_2", revision: "v1.0", - itemType: PigItemType.Entity, + itemType: PigItemType.anEntity, modified: new Date(), creator: "test_user", title: { text: "Title of Entity 2", lang: "en" }, - description: { text: "Description of d:entity_2", lang: "en" }, + description: { text: "Description of d:anEntity_2", lang: "en" }, - hasClass: "o:entityClass_1", + hasClass: "o:Entity_1", hasProperty: [{ - // itemType: PigItemType.aProperty, + itemType: PigItemType.aProperty, hasClass: "dcterms:type", - value: "Category of Entity_2" + value: "Category of d:anEntity_2" }] }; // Relationship with class: relationshipClass_input = { - id: "o:relationshipClass", + id: "o:Relationship", itemType: PigItemType.Relationship, title: { text: "Title of RelationshipClass", lang: "en" }, - description: { text: "Description of o:relationshipClass", lang: "en" }, + description: { text: "Description of o:Relationship", lang: "en" }, - eligibleSource: ["o:entityClass_1"], - eligibleTarget: ["o:entityClass_1"], + eligibleSource: ["o:Entity_1"], + eligibleTarget: ["o:Entity_1"], eligibleProperty: ["dcterms:type"] }; relationship_input = { id: "d:aRelationship_1", - itemType: PigItemType.Relationship, - hasClass: "o:relationshipClass", + itemType: PigItemType.aRelationship, + hasClass: "o:Relationship", revision: "v1.0", modified: new Date(), creator: "test_user", - title: { text: "Title of aRelationship_1", lang: "en" }, - description: { text: "Description of aRelationship_1", lang: "en" }, + title: { text: "Title of d:aRelationship_1", lang: "en" }, + description: { text: "Description of d:aRelationship_1", lang: "en" }, - hasSource: "d:anEntity_1", - hasTarget: "d:anEntity_2", + hasSource: { "itemType": "pig:aReference", "hasClass": "o:relates", "element": "d:anEntity_1" }, + hasTarget: { "itemType": "pig:aReference", "hasClass": "o:relates", "element": "d:anEntity_2" }, hasProperty: [{ - // itemType: PigItemType.aProperty, + itemType: PigItemType.aProperty, hasClass: "dcterms:title", value: "Name for Relationship_1" }] @@ -119,10 +129,11 @@ describe("PIG Metaclasses", () => { }); - test("Test class pig:Propery", () => { - const test_PC = new Property(propertyClass_input); + test("Test class pig:Property", () => { + const test_PC = new Property().set(propertyClass_input); - // check the attribute values: + // check the attribute values upon creation: + expect(test_PC.status().ok).toBe(true); expect(test_PC.id).toBe("dcterms:type"); expect(test_PC.title).toEqual({ text: "The type or category", lang: "en" }); expect(test_PC.description).toEqual({ text: "This is a class for a property named dcterms:type for use by anEntity or aRelationship", lang: "en" }); @@ -136,14 +147,17 @@ describe("PIG Metaclasses", () => { // check the output: const propertyClass_output = test_PC.get(); - expect(propertyClass_output).toEqual(Object.assign({ itemType: "pig:Property" }, propertyClass_input)); // itemType is added at object creation + expect(propertyClass_output).toEqual(propertyClass_input); + + const propertyClass_output_JSONLD = test_PC.getJSONLD(); + expect(propertyClass_output_JSONLD).toEqual(propertyClass_input_JSONLD); }); test("Test instance pig:aProperty", () => { // NOTE: aProperty needs to reference PropertClass but there is no validation in either class to ensure that // either of them exists. - const test_P = new AProperty(property_input); + const test_P = new AProperty().set(property_input); // check the attribute values: // expect(test_P.itemType).toBe(PigItemType.aProperty); @@ -153,44 +167,58 @@ describe("PIG Metaclasses", () => { }); test("Test class pig:Entity", () => { - const test_EC = new Entity(entityClass_input); + const test_EC = new Entity().set(entityClass_input); // check the attribute values: - expect(test_EC.id).toBe('o:entityClass_1'); + expect(test_EC.id).toBe('o:Entity_1'); expect(test_EC.title).toEqual({ text: 'Title of Entity Class 1', lang: "en" }); - expect(test_EC.description).toEqual({ text: 'Description of o:entityClass_1', lang: "en" }); + expect(test_EC.description).toEqual({ text: 'Description of o:Entity_1', lang: "en" }); expect(test_EC.itemType).toBe(PigItemType.Entity); expect(test_EC.eligibleProperty).toStrictEqual(["dcterms:type"]); // check the output: const entityClass_output = test_EC.get(); - expect(entityClass_output).toEqual(Object.assign({ itemType: "pig:Entity" }, entityClass_input)); // itemType is added at object creation + expect(entityClass_output).toEqual(entityClass_input); }); test("Test class pig:Relationship", () => { // NOTE: Relationship needs to reference two Entity objects but there is no validation in either class to ensure that // either of them exists. - const test_RC = new Relationship(relationshipClass_input); + const test_RC = new Relationship().set(relationshipClass_input); // check the attribute values: // expect(test_RC.validate(relationshipClass_input)).toBe(0); - expect(test_RC.id).toBe('o:relationshipClass'); + expect(test_RC.id).toBe('o:Relationship'); expect(test_RC.title).toEqual({ text: 'Title of RelationshipClass', lang: "en" }); - expect(test_RC.description).toEqual({ text: 'Description of o:relationshipClass', lang: "en" }); + expect(test_RC.description).toEqual({ text: 'Description of o:Relationship', lang: "en" }); expect(test_RC.itemType).toBe(PigItemType.Relationship); - expect(test_RC.eligibleTarget).toStrictEqual(['o:entityClass_1']); - expect(test_RC.eligibleSource).toStrictEqual(['o:entityClass_1']); + expect(test_RC.eligibleTarget).toStrictEqual(['o:Entity_1']); + expect(test_RC.eligibleSource).toStrictEqual(['o:Entity_1']); expect(test_RC.eligibleProperty).toStrictEqual(["dcterms:type"]); // check the output: const relationshipClass_output = test_RC.get(); - expect(relationshipClass_output).toEqual(Object.assign({ itemType: "pig:Relation" }, relationshipClass_input)); // itemType is added at object creation + expect(relationshipClass_output).toEqual(relationshipClass_input); }); - /* ToDo: ... more tests to come */ + // Synchronous Exception (Function or Constructor) + test('throws when itemType is invalid', () => { + expect(() => { + // do not add a completly bad input to avoid a TS error: + new Property().set(Object.assign({}, propertyClass_input, {itemType: 'bad'})); + }).toThrow(); // checks only that an exception is thrown + + /* // more detailed checking: Message, Regex oder Error-Konstruktor + expect(() => new Property().set(badInput as any)).toThrow('Expected'); + expect(() => new Property().set(badInput as any)).toThrow(/Expected 'Property'/); + */ + }); + +/* ToDo: ... more tests to come */ + });