Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(vocabulary) : support multiple terms per concept/property #664

Merged
merged 7 commits into from
Jul 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16,562 changes: 12,332 additions & 4,230 deletions package-lock.json

Large diffs are not rendered by default.

27 changes: 24 additions & 3 deletions packages/concerto-vocabulary/lib/vocabulary.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,19 +100,40 @@ class Vocabulary {
* Gets the term for a concept, enum or property
* @param {string} declarationName the name of a concept or enum
* @param {string} [propertyName] the name of a property (optional)
* @param {string} [identifier] the identifier of the term (optional)
* @returns {string} the term or null if it does not exist
*/
getTerm(declarationName, propertyName) {
getTerm(declarationName, propertyName, identifier) {
const decl = this.content.declarations.find(d => Object.keys(d)[0] === declarationName);
if(!decl) {
return null;
}
if(!propertyName) {
return decl[declarationName];
return identifier ? decl[identifier] : decl[declarationName];
}
else {
const property = decl.properties ? decl.properties.find(d => d[propertyName]) : null;
return property ? property[propertyName] : null;
return property ? identifier ? property[identifier] : property[propertyName] : null;
}
}

/**
* Gets the terms for a concept, enum or property
* @param {string} declarationName the name of a concept or enum
* @param {string} [propertyName] the name of a property (optional)
* @returns {string} the term or null if it does not exist
*/
getElementTerms(declarationName, propertyName) {
const decl = this.content.declarations.find(d => Object.keys(d)[0] === declarationName);
if(!decl) {
return null;
}
if(!propertyName) {
return decl;
}
else {
const property = decl.properties ? decl.properties.find(d => d[propertyName]) : null;
return property;
}
}

Expand Down
186 changes: 143 additions & 43 deletions packages/concerto-vocabulary/lib/vocabularymanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,32 @@ class VocabularyManager {
* @param {string} locale the BCP-47 locale identifier
* @param {string} declarationName the name of a concept or enum
* @param {string} [propertyName] the name of a property (optional)
* @param {string} [identifier] the identifier of the term (optional)
* @returns {string} the term or null if it does not exist
*/
resolveTerm(modelManager, namespace, locale, declarationName, propertyName) {
resolveTerm(modelManager, namespace, locale, declarationName, propertyName, identifier) {
const modelFile = modelManager.getModelFile(namespace);
const classDecl = modelFile ? modelFile.getType(declarationName) : null;
const property = propertyName ? classDecl ? classDecl.getProperty(propertyName) : null : null;
return this.getTerm(property ? property.getNamespace() : namespace, locale, property ? property.getParent().getName() : declarationName, propertyName);
return this.getTerm(property ? property.getNamespace() : namespace, locale, property ? property.getParent().getName() : declarationName, propertyName, identifier);
}

/**
* Resolve the terms for a property, looking up terms from a more general vocabulary
* if required, and resolving properties using an object manager, allowing terms defined
* on super types to be automatically resolved.
* @param {ModelManager} modelManager the model manager
* @param {string} namespace the namespace
* @param {string} locale the BCP-47 locale identifier
* @param {string} declarationName the name of a concept or enum
* @param {string} [propertyName] the name of a property (optional)
* @returns {*} the terms or null if it does not exist
*/
resolveTerms(modelManager, namespace, locale, declarationName, propertyName) {
const modelFile = modelManager.getModelFile(namespace);
const classDecl = modelFile ? modelFile.getType(declarationName) : null;
const property = propertyName ? classDecl ? classDecl.getProperty(propertyName) : null : null;
return this.getTerms(property ? property.getNamespace() : namespace, locale, property ? property.getParent().getName() : declarationName, propertyName);
}

/**
Expand All @@ -195,28 +214,59 @@ class VocabularyManager {
* @param {string} locale the BCP-47 locale identifier
* @param {string} declarationName the name of a concept or enum
* @param {string} [propertyName] the name of a property (optional)
* @param {string} [identifier] the identifier of the term (optional)
* @returns {string} the term or null if it does not exist
*/
getTerm(namespace, locale, declarationName, propertyName) {
getTerm(namespace, locale, declarationName, propertyName, identifier) {
const voc = this.getVocabulary(namespace, locale);
let term = null;
if (voc) {
term = voc.getTerm(declarationName, propertyName);
term = voc.getTerm(declarationName, propertyName, identifier);
}
if (term) {
return term;
}
else {
const dashIndex = locale.lastIndexOf('-');
if (dashIndex >= 0) {
return this.getTerm(namespace, locale.substring(0, dashIndex), declarationName, propertyName);
return this.getTerm(namespace, locale.substring(0, dashIndex), declarationName, propertyName, identifier);
}
else {
return this.missingTermGenerator ? this.missingTermGenerator(namespace, locale, declarationName, propertyName) : null;
}
}
}

/**
* Gets the term for a concept, enum or property, looking up terms
* from a more general vocabulary if required
* @param {string} namespace the namespace
* @param {string} locale the BCP-47 locale identifier
* @param {string} declarationName the name of a concept or enum
* @param {string} [propertyName] the name of a property (optional)
* @returns {*} the terms or null if it does not exist
*/
getTerms(namespace, locale, declarationName, propertyName) {
const voc = this.getVocabulary(namespace, locale);
let term = null;
if (voc) {
term = voc.getElementTerms(declarationName, propertyName);
}
if (term) {
return term;
}
else {
const dashIndex = locale.lastIndexOf('-');
if (dashIndex >= 0) {
return this.getTerms(namespace, locale.substring(0, dashIndex), declarationName, propertyName);
}
else {
const missingKey = propertyName ? propertyName : declarationName;
return this.missingTermGenerator ? { [missingKey]: this.missingTermGenerator(namespace, locale, declarationName, propertyName) } : null;
}
}
}

/**
* Creates a DecoractorCommandSet with @Term decorators
* to decorate all model elements based on the vocabulary for a locale.
Expand All @@ -236,51 +286,101 @@ class VocabularyManager {

modelManager.getModelFiles().forEach(model => {
model.getAllDeclarations().forEach(decl => {
const term = this.resolveTerm(modelManager, model.getNamespace(), locale, decl.getName());
if (term) {
decoratorCommandSet.commands.push({
'$class': 'org.accordproject.decoratorcommands.Command',
'type': 'UPSERT',
'target': {
'$class': 'org.accordproject.decoratorcommands.CommandTarget',
'namespace': model.getNamespace(),
'declaration': decl.getName(),
},
'decorator': {
'$class': `${MetaModelNamespace}.Decorator`,
'name': 'Term',
'arguments': [
{
'$class': `${MetaModelNamespace}.DecoratorString`,
'value': term
const terms = this.resolveTerms(modelManager, model.getNamespace(), locale, decl.getName());
if (terms) {
Object.keys(terms).forEach( term => {
if(term === decl.getName()) {
decoratorCommandSet.commands.push({
'$class': 'org.accordproject.decoratorcommands.Command',
'type': 'UPSERT',
'target': {
'$class': 'org.accordproject.decoratorcommands.CommandTarget',
'namespace': model.getNamespace(),
'declaration': decl.getName(),
},
]
'decorator': {
'$class': `${MetaModelNamespace}.Decorator`,
'name': 'Term',
'arguments': [
{
'$class': `${MetaModelNamespace}.DecoratorString`,
'value': terms[term]
},
]
}
});
}
else if(term.localeCompare('properties')) {
decoratorCommandSet.commands.push({
'$class': 'org.accordproject.decoratorcommands.Command',
'type': 'UPSERT',
'target': {
'$class': 'org.accordproject.decoratorcommands.CommandTarget',
'namespace': model.getNamespace(),
'declaration': decl.getName(),
},
'decorator': {
'$class': `${MetaModelNamespace}.Decorator`,
'name': `Term_${term}`,
'arguments': [
{
'$class': `${MetaModelNamespace}.DecoratorString`,
'value': terms[term]
},
]
}
});
}
});
}

decl.getProperties?.().forEach(property => {
const propertyTerm = this.resolveTerm(modelManager, model.getNamespace(), locale, decl.getName(), property.getName());

if (propertyTerm) {
decoratorCommandSet.commands.push({
'$class': 'org.accordproject.decoratorcommands.Command',
'type': 'UPSERT',
'target': {
'$class': 'org.accordproject.decoratorcommands.CommandTarget',
'namespace': model.getNamespace(),
'declaration': decl.getName(),
'property': property.getName()
},
'decorator': {
'$class': `${MetaModelNamespace}.Decorator`,
'name': 'Term',
'arguments': [
{
'$class': `${MetaModelNamespace}.DecoratorString`,
'value': propertyTerm
const propertyTerms = this.resolveTerms(modelManager, model.getNamespace(), locale, decl.getName(), property.getName());
if (propertyTerms) {
Object.keys(propertyTerms).forEach( term => {
if(term === property.getName()) {
decoratorCommandSet.commands.push({
'$class': 'org.accordproject.decoratorcommands.Command',
'type': 'UPSERT',
'target': {
'$class': 'org.accordproject.decoratorcommands.CommandTarget',
'namespace': model.getNamespace(),
'declaration': decl.getName(),
'property': property.getName()
},
'decorator': {
'$class': `${MetaModelNamespace}.Decorator`,
'name': 'Term',
'arguments': [
{
'$class': `${MetaModelNamespace}.DecoratorString`,
'value': propertyTerms[term]
},
]
}
});
}
else {
decoratorCommandSet.commands.push({
'$class': 'org.accordproject.decoratorcommands.Command',
'type': 'UPSERT',
'target': {
'$class': 'org.accordproject.decoratorcommands.CommandTarget',
'namespace': model.getNamespace(),
'declaration': decl.getName(),
'property': property.getName()
},
]
'decorator': {
'$class': `${MetaModelNamespace}.Decorator`,
'name': `Term_${term}`,
'arguments': [
{
'$class': `${MetaModelNamespace}.DecoratorString`,
'value': propertyTerms[term]
},
]
}
});
}
});
}
Expand Down
Loading