Skip to content

Commit

Permalink
RUBY: Merge pull request #10 from rioug/fix-product-types-parsing
Browse files Browse the repository at this point in the history
Improve SKOS Concept parsing to allow use of intermediate SKOS Concept.
  • Loading branch information
lecoqlibre authored Dec 11, 2023
2 parents abbf678 + 905c8b8 commit 64e2089
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 164 deletions.
11 changes: 6 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

Ruby:
- Fix random code changes of the generated code by sorting elements (e36a923).
- Fix some tests so they can pass by changing the expected JSON (e36a923).
- Enable loading of measures v1.0.2 (6ef17f7).
- Fix random code changes of the generated code by sorting elements (e36a9236fa012f87946b34c36cd463709d1cd2c5).
- Fix some tests so they can pass by changing the expected JSON (e36a9236fa012f87946b34c36cd463709d1cd2c5).
- Enable loading of measures v1.0.2 (6ef17f7d4a19aebd9d89b544db115d36f7e6fe93).
- Improve SKOS Concept parsing ([PR #10](https://github.com/datafoodconsortium/connector-codegen/pull/10)).

### Changed

Ruby:
- Preload context to avoid remote loading (4741acb).
- Preload context to avoid remote loading (4741acb7b2a396dc56f66f0c3b5e2d64078c7130).

## [1.0.1] - 2023-11-06

Expand Down Expand Up @@ -45,4 +46,4 @@ This release requires [data-model-uml version 2.1.0](https://github.com/datafood

[unreleased]: https://github.com/datafoodconsortium/connector-codegen/compare/v1.0.0...HEAD
[1.0.1]: https://github.com/datafoodconsortium/connector-codegen/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/datafoodconsortium/connector-codegen/releases/tag/v1.0.0
[1.0.0]: https://github.com/datafoodconsortium/connector-codegen/releases/tag/v1.0.0
5 changes: 4 additions & 1 deletion src/org/datafoodconsortium/connector/codegen/queries.mtl
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@
[query public isPrimitive(t: Type): Boolean = if (t.name = 'String' or t.name = 'Boolean' or t.name = 'Real' or t.name = 'Integer') then true else false endif /]
[query public isPrimitive(anElement: TypedElement): Boolean = anElement.type.isPrimitive() /]

[query public isSKOSConceptClass(c: Classifier): Boolean = (c.name = 'SKOSConcept') /]
[query public isSKOSConceptPrefLabel(p: Property): Boolean = (p.name = 'prefLabels') /]

[comment query public getAttributeForStereotypedOperation(c: Class, s: String, o: String): String
= invoke('org.datafoodconsortium.connector.codegen.common.Common', 'getAttributeForStereotypedOperation(org.eclipse.uml2.uml.Class, java.lang.String, java.lang.String)', Sequence{c, s, o})
/]
Expand Down Expand Up @@ -190,4 +193,4 @@

[comment query public getUnimplementedOperations(c: Classifier): Sequence(Operation)
= invoke('org.datafoodconsortium.connector.codegen.common.Common', 'getUnimplementedOperations(org.eclipse.uml2.uml.Classifier)', Sequence{c})
/]
/]
3 changes: 2 additions & 1 deletion src/org/datafoodconsortium/connector/codegen/ruby/class.mtl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
class DataFoodConsortium::Connector::[aClass.name.toUpperFirst()/][generateGeneralization(aClass)/]

[if (aClass.isSemantic() and aClass.generalization->isEmpty())]include VirtualAssembly::Semantizer::SemanticObject[/if]

[if (aClass.isSKOSConceptClass())]include DataFoodConsortium::Connector::SKOSHelper[/if]

[for (property: Property | aClass.ownedAttribute) separator('\n')]
# @return ['['/][property.type.name /][']'/]
attr_accessor :[property.name /]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
[comment encoding = UTF-8 /]
[module classifier('http://www.eclipse.org/uml2/5.0.0/UML')]

[import org::datafoodconsortium::connector::codegen::queries /]
[import org::datafoodconsortium::connector::codegen::ruby::common /]

[template public generateImports(classifier: Classifier)]
[for (ei: ElementImport | classifier.elementImport->sortedBy(i: ElementImport | i.importedElement.name)) separator('\n')][if (ei.importedElement.oclIsTypeOf(Class))][generateImport(ei)/][/if][/for]
[if (classifier.oclIsTypeOf(Class))]require "virtual_assembly/semantizer"[/if]
[if (classifier.isSKOSConceptClass())]require 'datafoodconsortium/connector/skos_helper'[/if]
[/template]

[template public generateImport(ei: ElementImport)]require "datafoodconsortium/connector/[generateFileName(ei.importedElement.name) /]"[/template]
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ end

[template public generateConstructorBody(class: Class, operation: Operation)]
[if not (class.generalization->isEmpty()) or class.isSemantic()]super([generateConstructorSuper(operation, class)/])[/if]
[for (p: Property | class.ownedAttribute) separator('\n')]@[p.name /] = [p.name /][/for]
[for (p: Property | class.ownedAttribute) separator('\n')][if (p.isSKOSConceptPrefLabel())]# Sort locale alphabetically
@[p.name /] = [p.name /].sort.to_h[else]@[p.name /] = [p.name /][/if][/for]

[if (class.isSemantic())][generateSetSemanticType(class)/][/if]
[for (property: Property | class.ownedAttribute->select(p: Property | p.isSemantic())) separator('\n')][generateSemanticProperty(property)/][/for]
[/template]
Expand Down Expand Up @@ -64,4 +66,4 @@ raise "Not yet implemented."

[template public genOperationParameter(parameter: Parameter)]
[if (parameter.direction = ParameterDirectionKind::_in)][parameter.name/][/if]
[/template]
[/template]
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- Fix random code changes of the generated code by sorting elements (e36a923).
- Fix some tests so they can pass by changing the expected JSON (e36a923).
- Enable loading of measures v1.0.2 (6ef17f7).
- Fix random code changes of the generated code by sorting elements (e36a9236fa012f87946b34c36cd463709d1cd2c5).
- Fix some tests so they can pass by changing the expected JSON (e36a9236fa012f87946b34c36cd463709d1cd2c5).
- Enable loading of measures v1.0.2 (6ef17f7d4a19aebd9d89b544db115d36f7e6fe93).
- Allow output context to be configured ([PR #8](https://github.com/datafoodconsortium/connector-codegen/pull/8)).
- Improve SKOS Concept parsing ([PR #10](https://github.com/datafoodconsortium/connector-codegen/pull/10)).

### Changed

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# frozen_string_literal: true

module DataFoodConsortium::Connector::SKOSHelper
def addAttribute(name, value)
self.instance_variable_set("@#{name}", value)
self.define_singleton_method(name) do
instance_variable_get("@#{name}")
end
end

def hasAttribute(name)
self.methods.include?(:"#{name}")
end
end
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
# frozen_string_literal: true

# MIT License
#
#
# Copyright (c) 2023 Maxime Lecoq <maxime@lecoqlibre.fr>
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
Expand All @@ -20,124 +22,139 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

require 'datafoodconsortium/connector/skos_helper'
require 'datafoodconsortium/connector/skos_concept'
require 'datafoodconsortium/connector/skos_parser_element'

class DataFoodConsortium::Connector::SKOSInstance
include DataFoodConsortium::Connector::SKOSHelper

def addAttribute(name, value)
self.instance_variable_set("@#{name}", value)
self.define_singleton_method(name) do
return instance_variable_get("@#{name}")
end
end

def hasAttribute(name)
return self.methods.include?(:"#{name}")
end

# Return a list of singelton methods, ie the list of Concept available
def topConcepts
self.methods(false).sort
end
end

class DataFoodConsortium::Connector::SKOSParser
CONCEPT_SCHEMES = ["Facet", "productTypes"].freeze

def initialize
@results = DataFoodConsortium::Connector::SKOSInstance.new
@skosConcepts = {}
@rootElements = []
@broaders = {}
# Flag used to tell the parser to use SkosConcept object when parsing data from Concept Scheme
# defined in CONCEPT_SCHEMES
@useSkosConcept = false
end

def parse(data)
init

data.each do |element|
current = DataFoodConsortium::Connector::SKOSParserElement.new(element)

setSkosConceptFlag(current)

if current.isConcept? || current.isCollection?
if !@skosConcepts.has_key?(current.id)
concept = createSKOSConcept(current)
@skosConcepts[current.id] = concept
end

def initialize()
@results = DataFoodConsortium::Connector::SKOSInstance.new
@skosConcepts = {}
@rootElements = []
@broaders = {}
end

def parse(data)
init()

data.each do |element|
current = DataFoodConsortium::Connector::SKOSParserElement.new(element)

if (current.isConcept?() || current.isCollection?())
if (!@skosConcepts.has_key?(current.id))
concept = createSKOSConcept(current)
@skosConcepts[current.id] = concept
end

if (current.hasBroader())
current.broader.each do |broaderId|
if (!@broaders.has_key?(broaderId))
@broaders[broaderId] = []
end

@broaders[broaderId].push(current.id)
end

# No broader, save the concept to the root
else
@rootElements.push(current.id)
end
if current.hasBroader
current.broader.each do |broaderId|
if !@broaders.has_key?(broaderId)
@broaders[broaderId] = []
end
end

@rootElements.each do |rootElementId|
setResults(@results, rootElementId)
end

return @results
@broaders[broaderId].push(current.id)
end
# No broader, save the concept to the root
else
@rootElements.push(current.id)
end
end
end

protected

def createSKOSConcept(element)
skosConcept = DataFoodConsortium::Connector::SKOSConcept.new(element.id)
skosConcept.semanticType = element.type
return skosConcept
@rootElements.each do |rootElementId|
setResults(@results, rootElementId)
end

def getValueWithoutPrefix(property)
name = 'undefined'

if (!property.include?('http') && property.include?(':'))
name = property.split(':')[1]
elsif (property.include?('#'))
name = property.split('#')[1]
end
@results
end

name = name.gsub('-', '_');
protected

# workaround to fix concepts starting with a number
# see https://github.com/datafoodconsortium/connector-ruby/issues/3
# see https://github.com/datafoodconsortium/ontology/issues/66
if (name.match?("^[0-9]"))
name = "_" + name
end
def createSKOSConcept(element)
skosConcept = DataFoodConsortium::Connector::SKOSConcept.new(
element.id, broaders: element.broader, narrowers: element.narrower, prefLabels: element.label
)
skosConcept.semanticType = element.type

skosConcept
end

return name.upcase
def getValueWithoutPrefix(property)
name = 'undefined'

if !property.include?('http') && property.include?(':')
name = property.split(':')[1]
elsif property.include?('#')
name = property.split('#')[1]
end

private

def init()
@results = DataFoodConsortium::Connector::SKOSInstance.new
@skosConcepts = {}
@rootElements = []
@broaders = {}
name = name.gsub('-', '_')

# workaround to fix concepts starting with a number
# see https://github.com/datafoodconsortium/connector-ruby/issues/3
# see https://github.com/datafoodconsortium/ontology/issues/66
name = "_" + name if name.match?("^[0-9]")

name.upcase
end

private

def init
@results = DataFoodConsortium::Connector::SKOSInstance.new
@skosConcepts = {}
@rootElements = []
@broaders = {}
@useSkosConcept = false
end

def setResults(parent, id)
name = getValueWithoutPrefix(id)

if !parent.hasAttribute(name)
if @useSkosConcept && @skosConcepts[id]
parent.addAttribute(name, @skosConcepts[id])
else
parent.addAttribute(name, DataFoodConsortium::Connector::SKOSInstance.new)
end
end

def setResults(parent, id)
name = getValueWithoutPrefix(id)
# Leaf concepts, stop the process
if !@broaders.has_key?(id)
parent.instance_variable_set("@#{name}", @skosConcepts[id])
return
end

if (!parent.hasAttribute(name))
parent.addAttribute(name, DataFoodConsortium::Connector::SKOSInstance.new)
end

# Leaf concepts, stop the process
if (!@broaders.has_key?(id))
parent.instance_variable_set("@#{name}", @skosConcepts[id])
return
end
@broaders[id].each do |narrower|
parentSkosInstance = parent.instance_variable_get("@#{name}")

@broaders[id].each do |narrower|
childName = getValueWithoutPrefix(narrower)
parentSkosInstance = parent.instance_variable_get("@#{name}")
setResults(parentSkosInstance, narrower) # recursive call
end
setResults(parentSkosInstance, narrower) # recursive call
end
end

end
def setSkosConceptFlag(current)
@useSkosConcept = true if current.isConceptScheme? && matchingConceptSchemes(current)
end

def matchingConceptSchemes(current)
regex = /#{CONCEPT_SCHEMES.join("|")}/

current.id =~ regex
end
end
Loading

0 comments on commit 64e2089

Please sign in to comment.