sparnatural
Version:
Visual client-side SPARQL query builder and knowledge graph exploration tool
330 lines • 21.1 kB
JavaScript
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _WidgetFactory_instances, _WidgetFactory_getFinalQueryString;
import { Config } from "../../../../../ontologies/SparnaturalConfig";
import { Datasources } from "../../../../../ontologies/SparnaturalConfigDatasources";
import { AutoCompleteWidget } from "../../../../widgets/AutoCompleteWidget";
import { BooleanWidget } from "../../../../widgets/BooleanWidget";
import { ListSparqlTemplateQueryBuilder, AutocompleteSparqlTemplateQueryBuilder, TreeSparqlTemplateQueryBuilder, ValuesListSparqlTemplateQueryBuilder } from "../../../../datasources/SparqlBuilders";
import { ListWidget } from "../../../../widgets/ListWidget";
import MapWidget from "../../../../widgets/MapWidget";
import { NoWidget } from "../../../../widgets/NoWidget";
import { NumberWidget } from "../../../../widgets/NumberWidget";
import { SearchRegexWidget } from "../../../../widgets/SearchRegexWidget";
import { TimeDatePickerWidget } from "../../../../widgets/timedatepickerwidget/TimeDatePickerWidget";
import { TreeWidget } from "../../../../widgets/treewidget/TreeWidget";
import { SortListDataProvider, SortTreeDataProvider, SortValuesListDataProvider } from "../../../../datasources/DataProviders";
import { NoOpListDataProvider, NoOpAutocompleteProvider, NoOpTreeDataProvider } from "../../../../datasources/NoOpDataProviders";
import { SparqlListDataProvider, SparqlAutocompleDataProvider, SparqlTreeDataProvider, SparqlValuesListDataProvider } from "../../../../datasources/SparqlDataProviders";
import { RDFS, SKOS, SparqlHandlerFactory } from "rdf-shacl-commons";
/**
* Inversion of coupling : we don't want to depend on ISettings as this class is meant to be reused
* elsewhere than in Sparnatural, hence we define our own interface of what we depend on.
*/
export class WidgetFactorySettings {
}
export class WidgetFactory {
constructor(parentComponent, specProvider, settings, catalog) {
_WidgetFactory_instances.add(this);
this.parentComponent = parentComponent;
this.specProvider = specProvider;
this.settings = settings;
this.catalog = catalog;
// how to handle / execute a SPARQL query
this.sparqlHandlerFactory = new SparqlHandlerFactory(this.settings.language, this.settings.localCacheDataTtl, this.settings.customization.headers, this.settings.customization.sparqlHandler, this.catalog);
// how to post-process the generated SPARQL after it is constructed and before it is send
this.sparqlPostProcessor = { semanticPostProcess: (sparql) => {
// also add prefixes
for (let key in this.settings.sparqlPrefixes) {
sparql = sparql.replace("SELECT ", "PREFIX " +
key +
": <" +
this.settings.sparqlPrefixes[key] +
"> \nSELECT ");
}
return this.specProvider.expandSparql(sparql, this.settings.sparqlPrefixes);
}
};
}
/**
* Creates a widget for the specified criteria subject / predicate / object
*
* @param startClassVal the type + variable name of the subject
* @param objectPropVal the type + variable name of the property
* @param endClassVal the type + variable name of the object
* @returns the widget component to be displayed
*/
buildWidget(startClassVal, objectPropVal, endClassVal) {
let property = this.specProvider.getProperty(objectPropVal.type);
// read the widgetType from the config
const widgetType = property.getPropertyType(endClassVal.type);
switch (widgetType) {
case Config.LITERAL_LIST_PROPERTY:
case Config.LIST_PROPERTY:
// determine custom datasource
var datasource = property.getDatasource();
if (datasource == null) {
// datasource still null
// if a default endpoint was provided, provide default datasource
if (this.settings.endpoints || this.settings.catalog) {
// if there are some values specified in the config with sh:in, use a values datasource
if (property.getValues()) {
datasource = {
queryTemplate: Datasources.QUERY_STRINGS_BY_QUERY_TEMPLATE.get(Datasources.QUERY_LIST_LABEL_VALUES_ALPHA),
labelPath: "<" + RDFS.LABEL.value + ">|<" + SKOS.PREF_LABEL.value + ">"
};
}
// if there is a default label property on the end class, use it to populate the dropdown
else if (this.specProvider.getEntity(endClassVal.type).getDefaultLabelProperty()) {
let defautlDatasource = property.hasQualifiedValueShapeRange() ? Datasources.QUERY_LIST_LABEL_WITH_RANGE_ALPHA : Datasources.QUERY_LIST_LABEL_ALPHA;
datasource = {
queryTemplate: Datasources.QUERY_STRINGS_BY_QUERY_TEMPLATE.get(defautlDatasource),
labelProperty: this.specProvider.getEntity(endClassVal.type).getDefaultLabelProperty(),
};
// if there is no datasource and no default label on the end class, but we can guess the end class could be skos:Concept,
// use a skos:prefLabel datasource
}
else if (this.specProvider.getEntity(endClassVal.type).couldBeSkosConcept()) {
let defautlDatasource = property.hasQualifiedValueShapeRange() ? Datasources.QUERY_LIST_LABEL_WITH_RANGE_ALPHA : Datasources.QUERY_LIST_LABEL_ALPHA;
datasource = {
queryTemplate: Datasources.QUERY_STRINGS_BY_QUERY_TEMPLATE.get(defautlDatasource),
labelProperty: SKOS.PREF_LABEL.value,
};
}
else {
// that datasource can work indifferently with URIs or Literals
datasource = Datasources.DATASOURCES_CONFIG.get(
// better use alphabetical ordering first since URIs will be segregated in the "h" letter and not mixed
// Datasources.LIST_URI_OR_LITERAL_ALPHA_WITH_COUNT
Datasources.LIST_URI_OR_LITERAL_ALPHA);
}
}
}
let listDataProvider = new NoOpListDataProvider();
if (datasource != null) {
if (property.getValues()) {
// if there was some fixed values specified in the config, use a ValuesListDataProvider
listDataProvider = new SparqlValuesListDataProvider(
// endpoint URL
this.sparqlHandlerFactory.buildSparqlHandler(datasource.sparqlEndpointUrl != null
? [datasource.sparqlEndpointUrl]
: this.settings.endpoints), new ValuesListSparqlTemplateQueryBuilder(
// sparql query (with labelPath interpreted)
__classPrivateFieldGet(this, _WidgetFactory_instances, "m", _WidgetFactory_getFinalQueryString).call(this, datasource),
// sparqlPostProcessor
this.sparqlPostProcessor));
// if we need to sort things, add an explicit wrapper around the data provider
if (!(datasource.noSort == true)) {
listDataProvider = new SortValuesListDataProvider(listDataProvider);
}
}
else {
// if we have a datasource, possibly the default one, provide a config based
// on a SparqlTemplate, otherwise use the handler provided
listDataProvider = new SparqlListDataProvider(
// endpoint URL
this.sparqlHandlerFactory.buildSparqlHandler(datasource.sparqlEndpointUrl != null
? [datasource.sparqlEndpointUrl]
: this.settings.endpoints), new ListSparqlTemplateQueryBuilder(
// sparql query (with labelPath interpreted)
__classPrivateFieldGet(this, _WidgetFactory_instances, "m", _WidgetFactory_getFinalQueryString).call(this, datasource),
// sparqlPostProcessor
this.sparqlPostProcessor));
// if we need to sort things, add an explicit wrapper around the data provider
if (!(datasource.noSort == true)) {
listDataProvider = new SortListDataProvider(listDataProvider);
}
}
}
// create the configuration object : use the default configuration, then the generated data provider, then overwrite with
// what is set in the provided configuration object for the corresponding section
let listConfig = {
...ListWidget.defaultConfiguration,
...{
dataProvider: listDataProvider,
values: this.specProvider.getProperty(objectPropVal.type).getValues(),
},
...this.settings.customization?.list
};
// init data provider
listConfig.dataProvider.init(this.settings.language, this.settings.defaultLanguage, this.settings.typePredicate);
return new ListWidget(this.parentComponent, listConfig, startClassVal, objectPropVal, endClassVal);
case Config.AUTOCOMPLETE_PROPERTY:
// to be passed in anonymous functions
var theSpecProvider = this.specProvider;
// determine custom datasource
var datasource = this.specProvider.getProperty(objectPropVal.type).getDatasource();
if (datasource == null) {
// datasource still null
// if a default endpoint was provided, provide default datasource
if (this.settings.endpoints) {
if (this.specProvider.getEntity(endClassVal.type).isLiteralEntity()) {
datasource = Datasources.DATASOURCES_CONFIG.get(Datasources.SEARCH_LITERAL_CONTAINS);
}
else {
// if there is a default label property on the end class, use it to search in the autocomplete
if (this.specProvider.getEntity(endClassVal.type).getDefaultLabelProperty()) {
datasource = {
queryTemplate: Datasources.QUERY_STRINGS_BY_QUERY_TEMPLATE.get(Datasources.QUERY_SEARCH_LABEL_CONTAINS),
labelProperty: this.specProvider.getEntity(endClassVal.type).getDefaultLabelProperty(),
};
}
else {
// otherwise just search on the URI
datasource = Datasources.DATASOURCES_CONFIG.get(Datasources.SEARCH_URI_CONTAINS);
}
}
}
}
let autocompleteDataProvider = new NoOpAutocompleteProvider();
if (datasource != null) {
// build a SPARQL data provider function using the SPARQL query of the datasource
autocompleteDataProvider = new SparqlAutocompleDataProvider(
// endpoint URL
this.sparqlHandlerFactory.buildSparqlHandler(datasource.sparqlEndpointUrl != null
? [datasource.sparqlEndpointUrl]
: this.settings.endpoints), new AutocompleteSparqlTemplateQueryBuilder(
// sparql query (with labelPath interpreted)
__classPrivateFieldGet(this, _WidgetFactory_instances, "m", _WidgetFactory_getFinalQueryString).call(this, datasource),
// sparqlPostProcessor
this.sparqlPostProcessor));
}
// create the configuration object : use the default data provider, then the default configuration, then overwrite with is set in
// the provided configuration object for the corresponding section
let autocompleteConfig = {
...AutoCompleteWidget.defaultConfiguration,
...{ dataProvider: autocompleteDataProvider },
...this.settings.customization?.autocomplete
};
// init data provider
autocompleteConfig.dataProvider.init(this.settings.language, this.settings.defaultLanguage, this.settings.typePredicate);
return new AutoCompleteWidget(this.parentComponent, autocompleteConfig, startClassVal, objectPropVal, endClassVal);
break;
case Config.VIRTUOSO_SEARCH_PROPERTY:
case Config.GRAPHDB_SEARCH_PROPERTY:
case Config.STRING_EQUALS_PROPERTY:
case Config.SEARCH_PROPERTY:
let configuration = {
widgetType: widgetType
};
return new SearchRegexWidget(configuration, this.parentComponent, startClassVal, objectPropVal, endClassVal);
break;
case Config.TIME_PROPERTY_YEAR:
return new TimeDatePickerWidget(this.parentComponent, "year", startClassVal, objectPropVal, endClassVal, this.specProvider);
break;
case Config.TIME_PROPERTY_DATE:
return new TimeDatePickerWidget(this.parentComponent, "day", startClassVal, objectPropVal, endClassVal, this.specProvider);
break;
case Config.TIME_PROPERTY_PERIOD:
console.warn(Config.TIME_PROPERTY_PERIOD + " is not implement yet");
break;
case Config.NON_SELECTABLE_PROPERTY:
return new NoWidget(this.parentComponent);
break;
case Config.BOOLEAN_PROPERTY:
let booleanConfig = {
existNotExist: !this.specProvider.getEntity(endClassVal.type).isLiteralEntity()
};
return new BooleanWidget(this.parentComponent, booleanConfig, startClassVal, objectPropVal, endClassVal);
break;
case Config.TREE_PROPERTY:
var theSpecProvider = this.specProvider;
// determine custom roots datasource
var treeRootsDatasource = this.specProvider.getProperty(objectPropVal.type).getTreeRootsDatasource();
if (treeRootsDatasource == null) {
// datasource still null
// if a default endpoint was provided, provide default datasource
if (this.settings.endpoints) {
treeRootsDatasource = Datasources.DATASOURCES_CONFIG.get(Datasources.TREE_ROOT_SKOSTOPCONCEPT);
}
}
// determine custom children datasource
var treeChildrenDatasource = this.specProvider.getProperty(objectPropVal.type).getTreeChildrenDatasource();
if (treeChildrenDatasource == null) {
// datasource still null
// if a default endpoint was provided, provide default datasource
if (this.settings.endpoints) {
treeChildrenDatasource = Datasources.DATASOURCES_CONFIG.get(Datasources.TREE_CHILDREN_SKOSNARROWER);
}
}
let treeDataProvider = new NoOpTreeDataProvider();
if (treeRootsDatasource != null && treeChildrenDatasource != null) {
// if we have a datasource, possibly the default one, provide a config based
// on a SparqlTemplate, otherwise use the handler provided
treeDataProvider = new SparqlTreeDataProvider(
// endpoint URL
// we read it on the roots datasource
this.sparqlHandlerFactory.buildSparqlHandler(treeRootsDatasource.sparqlEndpointUrl != null
? [treeRootsDatasource.sparqlEndpointUrl]
: this.settings.endpoints), new TreeSparqlTemplateQueryBuilder(
// sparql query (with labelPath interpreted)
__classPrivateFieldGet(this, _WidgetFactory_instances, "m", _WidgetFactory_getFinalQueryString).call(this, treeRootsDatasource), __classPrivateFieldGet(this, _WidgetFactory_instances, "m", _WidgetFactory_getFinalQueryString).call(this, treeChildrenDatasource),
// sparqlPostProcessor
this.sparqlPostProcessor));
}
// create the configuration object : use the default data provider, then the default configuration, then overwrite with is set in
// the provided configuration object for the corresponding section
let treeConfig = {
...TreeWidget.defaultConfiguration,
...{
dataProvider: treeDataProvider,
maxSelectedItems: this.settings.maxOr
},
...this.settings.customization?.tree
};
// wrap inside a sort data provider if needed
if (!(treeChildrenDatasource.noSort == true)) {
treeConfig.dataProvider = new SortTreeDataProvider(treeConfig.dataProvider);
}
// init data provider
treeConfig.dataProvider.init(this.settings.language, this.settings.defaultLanguage, this.settings.typePredicate);
return new TreeWidget(this.parentComponent, treeConfig, startClassVal, objectPropVal, endClassVal);
case Config.MAP_PROPERTY:
let mapConfig = {
...MapWidget.defaultConfiguration,
...this.settings.customization?.map
};
return new MapWidget(mapConfig, this.parentComponent, startClassVal, objectPropVal, endClassVal).render();
case Config.NUMBER_PROPERTY:
let thisNumberConfig = {
min: this.specProvider.getProperty(objectPropVal.type).getMinValue(),
max: this.specProvider.getProperty(objectPropVal.type).getMaxValue(),
};
let numberConfig = {
...NumberWidget.defaultConfiguration,
...thisNumberConfig,
...this.settings.customization?.number
};
return new NumberWidget(this.parentComponent, numberConfig, startClassVal, objectPropVal, endClassVal).render();
default:
throw new Error(`WidgetType for ${widgetType} not recognized`);
}
}
}
_WidgetFactory_instances = new WeakSet(), _WidgetFactory_getFinalQueryString = function _WidgetFactory_getFinalQueryString(datasource) {
if (datasource.queryString != null) {
return datasource.queryString;
}
else {
var sparql = datasource.queryTemplate;
if (datasource.labelPath != null || datasource.labelProperty) {
var theLabelPath = datasource.labelPath
? datasource.labelPath
: "<" + datasource.labelProperty + ">";
var reLabelPath = new RegExp("\\$labelPath", "g");
sparql = sparql.replace(reLabelPath, theLabelPath);
}
if (datasource.childrenPath != null || datasource.childrenProperty) {
var theChildrenPath = datasource.childrenPath
? datasource.childrenPath
: "<" + datasource.childrenProperty + ">";
var reChildrenPath = new RegExp("\\$childrenPath", "g");
sparql = sparql.replace(reChildrenPath, theChildrenPath);
}
return sparql;
}
};
//# sourceMappingURL=WidgetFactory.js.map