Skip to content

Commit

Permalink
Return single values if OWL indicates the property's maximum cardinal…
Browse files Browse the repository at this point in the history
…ity is 1 (#22)

This commit uses the OWL reasoner in Jena to expand the property graph, and uses the Jena Ontology API to detect if the property being accessed has a maxCardinality restriction. It will limit the number of returned elements to that maxCardinality, and if it is 1, it will either return that value or `null` if no value exists.

This is in order to follow the conventions in the OWL ontologies produced by the MOF2RDF mapping. This specific use of restrictions (and other MOF2RDF conventions) will be moved in the future to a MOF2RDF-specific subclass of RDFModel, leaving RDFModel as a "pure RDF" model that acts as an Epsilon wrapper over Apache Jena.

---------

Co-authored-by: Antonio Garcia-Dominguez <[email protected]>
  • Loading branch information
OwenR-York and agarciadom authored Jan 17, 2025
1 parent f05488b commit 698f834
Show file tree
Hide file tree
Showing 27 changed files with 2,238 additions and 156 deletions.
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,11 @@ target-plain/
*.polyglot*
*.tycho
*.META-INF_MANIFEST.MF

# Eclipse IDE metadata in non-project folders
/.project
/bundles/.project
/examples/.project
/features/.project
/releng/.project
/tests/.project
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,27 @@ For instance, if we set the language preferences to `en-gb,en`, filtering `x.pro
* Otherwise, return the untagged literals (if any).

Language preferences do not apply if an explicit language tag is used: `x.property@en` will always get the `en`-tagged literals, and `x.property@` will always get the untagged literals.

### Data models, schema models and reasoners

RDF models are loaded as Ontology Resource Models with Jena's default OWL reasoner.
This reasoner infers OWL concepts onto an RDF data model when it is loaded.

In order to support OWL inferencing, the `RDF Model` configuration dialog is divided into two sections:

* "Data Model URLs to load": each of the elements in the list is loaded and merged into a single RDF data model.
* "Schema Model URLs to load": each element is merged into a single RDF schema model.

The resulting RDF data and schema models are then processed by Jena's reasoner using the default OWL settings.
The inferred model is then used by Epsilon for querying.

#### Schema-defined restrictions (maximum cardinality)

An RDF Schema can contain OWL restrictions for some properties in an RDF data model, such as maximum cardinality.

When computing `resource.property`, if a maximum cardinality is defined for `property`, then the number of returned values will be limited to that maximum size.
If there are multiple maximum cardinality restrictions, the most restrictive one will be used.

In the specific case that the maximum cardinality is 1, `resource.property` will directly return the value (if set) or `null`, instead of returning a collection.

Note: this behaviour will be moved to the MOF2RDF-specific variant of the driver in the future (see #15).
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import org.eclipse.swt.widgets.Text;

public class RDFModelConfigurationDialog extends AbstractModelConfigurationDialog {
private static final String[] RDFFILE_EXTENSIONS = new String[] { "*.rdf", "*.ttl", "*.nt", "*.nq", "*.trig", "*.owl", "*.jsonld", "*.trdf", "*.rt", "*.rpb", "*.pbrdf", "*.rj", "*.trix", "*.*"};

private static final String SAMPLE_URL = "http://changeme";

Expand Down Expand Up @@ -162,9 +163,12 @@ public URLTableEntry(String url) {
public String url;
}

private TableViewer urlList;
private List<URLTableEntry> urls = new ArrayList<>();
private TableViewer dataModelUrlListViewer;
private List<URLTableEntry> dataModelUrls = new ArrayList<>();

private TableViewer schemaModelUrlListViewer;
private List<URLTableEntry> schemaModelUrls = new ArrayList<>();

private TableViewer nsMappingTable;
private List<NamespaceMappingTableEntry> nsMappingEntries = new ArrayList<>();

Expand All @@ -181,7 +185,8 @@ protected String getModelType() {
@Override
protected void createGroups(Composite control) {
createNameAliasGroup(control);
createRDFUrlsGroup(control);
createDataModelRDFUrlsGroup(control);
createSchemaModelRDFUrlsGroup(control);
createNamespaceMappingGroup(control);
createLanguagePreferenceGroup(control);
}
Expand Down Expand Up @@ -269,27 +274,27 @@ public void widgetSelected(SelectionEvent e) {
return groupContent;
}

private String lastPath = null;
private Composite createRDFUrlsGroup(Composite parent) {
final Composite groupContent = DialogUtil.createGroupContainer(parent, "URLs to load", 2);
private String dataModelLastPath = null;
private Composite createDataModelRDFUrlsGroup(Composite parent) {
final Composite groupContent = DialogUtil.createGroupContainer(parent, "Data Model URLs to load", 2);

urlList = new TableViewer(groupContent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.MULTI);
dataModelUrlListViewer = new TableViewer(groupContent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.MULTI);

TableViewerColumn urlColumn = new TableViewerColumn(urlList, SWT.NONE);
TableViewerColumn urlColumn = new TableViewerColumn(dataModelUrlListViewer, SWT.NONE);
urlColumn.getColumn().setWidth(800);
urlColumn.setLabelProvider(new ColumnLabelProvider() {
@Override
public String getText(Object element) {
return ((URLTableEntry) element).url;
}
});
urlColumn.setEditingSupport(new URLEntryEditingSupport(urlList));
urlColumn.setEditingSupport(new URLEntryEditingSupport(dataModelUrlListViewer));

urlList.setContentProvider(ArrayContentProvider.getInstance());
urlList.setInput(urls);
dataModelUrlListViewer.setContentProvider(ArrayContentProvider.getInstance());
dataModelUrlListViewer.setInput(dataModelUrls);

GridData urlListLayout = new GridData(SWT.FILL, SWT.FILL, true, true);
urlList.getControl().setLayoutData(urlListLayout);
dataModelUrlListViewer.getControl().setLayoutData(urlListLayout);

final Composite urlButtons = new Composite(groupContent, SWT.NONE);
final GridData urlButtonsLayout = new GridData();
Expand All @@ -302,8 +307,8 @@ public String getText(Object element) {
addUrlButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
urls.add(new URLTableEntry(SAMPLE_URL));
urlList.refresh();
dataModelUrls.add(new URLTableEntry(SAMPLE_URL));
dataModelUrlListViewer.refresh();
}
});

Expand All @@ -316,8 +321,8 @@ public void widgetSelected(SelectionEvent e) {
"Browse workspace", "Select file with RDF content", "*.rdf", null);

if (file != null) {
urls.add(new URLTableEntry(file.getLocationURI().toString()));
urlList.refresh();
dataModelUrls.add(new URLTableEntry(file.getLocationURI().toString()));
dataModelUrlListViewer.refresh();
}
}
});
Expand All @@ -329,16 +334,16 @@ public void widgetSelected(SelectionEvent e) {
public void widgetSelected(SelectionEvent e) {
FileDialog fileDialog = new FileDialog(getShell(), SWT.OPEN);
fileDialog.setText("Select an RDF file to add");
fileDialog.setFilterExtensions(new String[] { ".rdf", ".ttl", ".nt", ".nq", ".trig", ".owl", ".jsonld", ".trdf", ".rt", ".rpb", ".pbrdf", ".rj", ".trix", ".*" });
if (lastPath != null)
fileDialog.setFilterPath(lastPath);
fileDialog.setFilterExtensions(RDFFILE_EXTENSIONS);
if (dataModelLastPath != null)
fileDialog.setFilterPath(dataModelLastPath);

String selectedFile = fileDialog.open();
if (selectedFile != null) {
urls.add(new URLTableEntry("file:" + selectedFile));
urlList.refresh();
dataModelUrls.add(new URLTableEntry("file:" + selectedFile));
dataModelUrlListViewer.refresh();
}
lastPath = fileDialog.getFilterPath();
dataModelLastPath = fileDialog.getFilterPath();
}
});

Expand All @@ -347,12 +352,12 @@ public void widgetSelected(SelectionEvent e) {
removeUrlButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (urlList.getSelection() instanceof IStructuredSelection) {
final IStructuredSelection sel = (IStructuredSelection)urlList.getSelection();
if (dataModelUrlListViewer.getSelection() instanceof IStructuredSelection) {
final IStructuredSelection sel = (IStructuredSelection)dataModelUrlListViewer.getSelection();
for (Iterator<?> it = sel.iterator(); it.hasNext(); ) {
urls.remove(it.next());
dataModelUrls.remove(it.next());
}
urlList.refresh();
dataModelUrlListViewer.refresh();
validateForm();
}
}
Expand All @@ -363,8 +368,113 @@ public void widgetSelected(SelectionEvent e) {
clearUrlButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
urls.clear();
urlList.refresh();
dataModelUrls.clear();
dataModelUrlListViewer.refresh();
validateForm();
}
});

groupContent.layout();
groupContent.pack();
return groupContent;
}

private String schemaModelLastPath = null;
private Composite createSchemaModelRDFUrlsGroup(Composite parent) {
final Composite groupContent = DialogUtil.createGroupContainer(parent, "Schema Model URLs to load", 2);

schemaModelUrlListViewer = new TableViewer(groupContent, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.MULTI);

TableViewerColumn urlColumn = new TableViewerColumn(schemaModelUrlListViewer, SWT.NONE);
urlColumn.getColumn().setWidth(800);
urlColumn.setLabelProvider(new ColumnLabelProvider() {
@Override
public String getText(Object element) {
return ((URLTableEntry) element).url;
}
});
urlColumn.setEditingSupport(new URLEntryEditingSupport(schemaModelUrlListViewer));

schemaModelUrlListViewer.setContentProvider(ArrayContentProvider.getInstance());
schemaModelUrlListViewer.setInput(schemaModelUrls);

GridData urlListLayout = new GridData(SWT.FILL, SWT.FILL, true, true);
schemaModelUrlListViewer.getControl().setLayoutData(urlListLayout);

final Composite urlButtons = new Composite(groupContent, SWT.NONE);
final GridData urlButtonsLayout = new GridData();
urlButtonsLayout.horizontalAlignment = SWT.FILL;
urlButtons.setLayoutData(urlButtonsLayout);
urlButtons.setLayout(new FillLayout(SWT.VERTICAL));

final Button addUrlButton = new Button(urlButtons, SWT.NONE);
addUrlButton.setText("Add");
addUrlButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
schemaModelUrls.add(new URLTableEntry(SAMPLE_URL));
schemaModelUrlListViewer.refresh();
}
});

final Button addFromWorkspaceButton = new Button(urlButtons, SWT.NONE);
addFromWorkspaceButton.setText("Browse Workspace...");
addFromWorkspaceButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
IFile file = BrowseWorkspaceUtil.browseFile(getShell(),
"Browse workspace", "Select file with RDF content", "*.rdf", null);

if (file != null) {
schemaModelUrls.add(new URLTableEntry(file.getLocationURI().toString()));
schemaModelUrlListViewer.refresh();
}
}
});

final Button addFromFileSystemButton = new Button(urlButtons, SWT.NONE);
addFromFileSystemButton.setText("Browse Filesystem...");
addFromFileSystemButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
FileDialog fileDialog = new FileDialog(getShell(), SWT.OPEN);
fileDialog.setText("Select an RDF file to add");
fileDialog.setFilterExtensions(RDFFILE_EXTENSIONS);
if (schemaModelLastPath != null)
fileDialog.setFilterPath(schemaModelLastPath);

String selectedFile = fileDialog.open();
if (selectedFile != null) {
schemaModelUrls.add(new URLTableEntry("file:" + selectedFile));
schemaModelUrlListViewer.refresh();
}
schemaModelLastPath = fileDialog.getFilterPath();
}
});

final Button removeUrlButton = new Button(urlButtons, SWT.NONE);
removeUrlButton.setText("Remove");
removeUrlButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (schemaModelUrlListViewer.getSelection() instanceof IStructuredSelection) {
final IStructuredSelection sel = (IStructuredSelection)schemaModelUrlListViewer.getSelection();
for (Iterator<?> it = sel.iterator(); it.hasNext(); ) {
schemaModelUrls.remove(it.next());
}
schemaModelUrlListViewer.refresh();
validateForm();
}
}
});

final Button clearUrlButton = new Button(urlButtons, SWT.NONE);
clearUrlButton.setText("Clear");
clearUrlButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
schemaModelUrls.clear();
schemaModelUrlListViewer.refresh();
validateForm();
}
});
Expand Down Expand Up @@ -403,10 +513,17 @@ protected void loadProperties(){
super.loadProperties();
if (properties == null) return;

urls.clear();
for (String url : properties.getProperty(RDFModel.PROPERTY_URIS).split("\\s*,\\s*")) {
dataModelUrls.clear();
for (String url : properties.getProperty(RDFModel.PROPERTY_DATA_URIS).split("\\s*,\\s*")) {
if (url.length() > 0) {
urls.add(new URLTableEntry(url));
dataModelUrls.add(new URLTableEntry(url));
}
}

schemaModelUrls.clear();
for (String url : properties.getProperty(RDFModel.PROPERTY_SCHEMA_URIS).split("\\s*,\\s*")) {
if (url.length() > 0) {
schemaModelUrls.add(new URLTableEntry(url));
}
}

Expand All @@ -425,7 +542,8 @@ protected void loadProperties(){

languagePreferenceText.setText(properties.getProperty(RDFModel.PROPERTY_LANGUAGE_PREFERENCE));

this.urlList.refresh();
this.dataModelUrlListViewer.refresh();
this.schemaModelUrlListViewer.refresh();
this.nsMappingTable.refresh();
validateForm();
}
Expand All @@ -434,10 +552,15 @@ protected void loadProperties(){
protected void storeProperties(){
super.storeProperties();

properties.put(RDFModel.PROPERTY_URIS,
String.join(",", urls.stream()
properties.put(RDFModel.PROPERTY_DATA_URIS,
String.join(",", dataModelUrls.stream()
.map(e -> e.url)
.collect(Collectors.toList())));

properties.put(RDFModel.PROPERTY_SCHEMA_URIS,
String.join(",", schemaModelUrls.stream()
.map(e -> e.url)
.collect(Collectors.toList())));

properties.put(RDFModel.PROPERTY_PREFIXES,
String.join(",", nsMappingEntries.stream()
Expand All @@ -464,14 +587,22 @@ protected void validateForm() {
}
}

for (URLTableEntry entry : this.urls) {
for (URLTableEntry entry : this.dataModelUrls) {
String errorMessage = validateURL(entry.url);
if (errorMessage != null) {
setErrorMessage(errorMessage);
return;
}
}

for (URLTableEntry entry : this.schemaModelUrls) {
String errorMessage = validateURL(entry.url);
if (errorMessage != null) {
setErrorMessage(errorMessage);
return;
}
}

for (NamespaceMappingTableEntry entry : this.nsMappingEntries) {
String errorMessage = validateURL(entry.url);
if (errorMessage != null) {
Expand Down
Loading

0 comments on commit 698f834

Please sign in to comment.