/*
* Copyright (c) 2013-2015 Josef Hardi <josef.hardi@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.obidea.semantika.mapping.parser.r2rml;
import static java.lang.String.format;
import io.github.johardi.r2rmlparser.R2RmlVocabulary;
import io.github.johardi.r2rmlparser.document.IMappingVisitor;
import io.github.johardi.r2rmlparser.document.LogicalTable;
import io.github.johardi.r2rmlparser.document.ObjectMap;
import io.github.johardi.r2rmlparser.document.PredicateMap;
import io.github.johardi.r2rmlparser.document.PredicateObjectMap;
import io.github.johardi.r2rmlparser.document.RefObjectMap;
import io.github.johardi.r2rmlparser.document.SubjectMap;
import io.github.johardi.r2rmlparser.document.TermMap;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import com.obidea.semantika.database.sql.parser.SqlFactory;
import com.obidea.semantika.datatype.DataType;
import com.obidea.semantika.datatype.exception.UnsupportedDataTypeException;
import com.obidea.semantika.exception.SemantikaRuntimeException;
import com.obidea.semantika.expression.ExpressionObjectFactory;
import com.obidea.semantika.expression.base.ITerm;
import com.obidea.semantika.expression.base.UriReference;
import com.obidea.semantika.mapping.IMetaModel;
import com.obidea.semantika.mapping.MappingObjectFactory;
import com.obidea.semantika.mapping.UriTemplate;
import com.obidea.semantika.mapping.base.TermType;
import com.obidea.semantika.mapping.base.sql.SqlColumn;
import com.obidea.semantika.mapping.base.sql.SqlQuery;
import com.obidea.semantika.mapping.parser.AbstractMappingHandler;
public class R2RmlMappingHandler extends AbstractMappingHandler implements IMappingVisitor
{
private SqlFactory mSqlFactory = new SqlFactory(getDatabaseMetadata());
private boolean mUseStrictParsing = false;
private static ExpressionObjectFactory sExpressionObjectFactory = ExpressionObjectFactory.getInstance();
private static MappingObjectFactory sMappingObjectFactory = MappingObjectFactory.getInstance();
public R2RmlMappingHandler(IMetaModel metaModel)
{
super(metaModel);
}
public void setStrictParsing(boolean useStrictParsing)
{
mUseStrictParsing = useStrictParsing;
}
public boolean isStrictParsing()
{
return mUseStrictParsing;
}
@Override
public void setSubjectUri(URI classUri)
{
checkClassSignature(classUri);
super.setSubjectUri(classUri);
}
@Override
public void setPredicateUri(URI propertyUri)
{
checkPropertySignature(propertyUri);
super.setPredicateUri(propertyUri);
}
@Override
public void visit(LogicalTable arg)
{
String sqlString = arg.getTableView().getSqlQuery();
setSqlQuery(createQuery(sqlString));
}
private SqlQuery createQuery(String sqlString)
{
return mSqlFactory.create(sqlString);
}
@Override
public void visit(SubjectMap arg)
{
validateSubjectMap(arg);
setSubjectUri(createUri(arg.getClassIri()));
int termMap = arg.getType();
String value = arg.getValue();
String termType = arg.getTermType();
String datatype = arg.getDatatype();
switch (termMap) {
case TermMap.COLUMN_VALUE:
setSubjectMapValue(getColumnTerm(value, termType, datatype));
break;
case TermMap.CONSTANT_VALUE:
setSubjectMapValue(getLiteralTerm(value, termType, datatype));
break;
case TermMap.TEMPLATE_VALUE:
setSubjectMapValue(getTemplateTerm(value, termType, datatype));
break;
}
// Create the class mapping if a class URI specified in the mapping
if (getSubjectUri() != null) {
addMapping(sMappingObjectFactory.createClassMapping(getSubjectUri(), getSqlQuery(), getSubjectMapValue()));
}
}
/*
* Validation procedure based on http://www.w3.org/TR/r2rml/#termtype
*/
private void validateSubjectMap(SubjectMap arg)
{
String termType = arg.getTermType();
if (termType.equals(R2RmlVocabulary.LITERAL)) {
throw new IllegalR2RmlMappingException("Subject map cannot have term type rr:Literal"); //$NON-NLS-1$
}
}
@Override
public void visit(PredicateObjectMap arg)
{
arg.getPredicateMap().accept(this);
arg.getObjectMap().accept(this);
addMapping(sMappingObjectFactory.createPropertyMapping(getPredicateUri(), getSqlQuery(), getSubjectMapValue(),
getObjectMapValue()));
}
@Override
public void visit(PredicateMap arg)
{
validatePredicateMap(arg);
int termMap = arg.getType();
String value = arg.getValue();
String termType = arg.getTermType();
String datatype = arg.getDatatype();
switch (termMap) {
case TermMap.COLUMN_VALUE:
throw new IllegalR2RmlMappingException("Predicate map cannot use column-valued term map"); //$NON-NLS-1$
case TermMap.CONSTANT_VALUE:
ITerm predicateTerm = getLiteralTerm(value, termType, datatype);
setPredicateUri(createUri(predicateTerm));
setPredicateMapValue(predicateTerm);
break;
case TermMap.TEMPLATE_VALUE:
throw new IllegalR2RmlMappingException("Predicate map cannot use template-valued term map"); //$NON-NLS-1$
}
}
/*
* Validation procedure based on http://www.w3.org/TR/r2rml/#termtype
*/
private void validatePredicateMap(PredicateMap arg)
{
String termType = arg.getTermType();
if (termType.equals(R2RmlVocabulary.LITERAL)) {
throw new IllegalR2RmlMappingException("Subject map cannot have term type rr:Literal"); //$NON-NLS-1$
}
else if (termType.equals(R2RmlVocabulary.BLANK_NODE)) {
throw new IllegalR2RmlMappingException("Subject map cannot have term type rr:BlankNode"); //$NON-NLS-1$
}
}
@Override
public void visit(ObjectMap arg)
{
int termMap = arg.getType();
String value = arg.getValue();
String termType = arg.getTermType();
String datatype = arg.getDatatype();
switch (termMap) {
case TermMap.COLUMN_VALUE:
setObjectMapValue(getColumnTerm(value, termType, datatype));
break;
case TermMap.CONSTANT_VALUE:
setObjectMapValue(getLiteralTerm(value, termType, datatype));
break;
case TermMap.TEMPLATE_VALUE:
setObjectMapValue(getTemplateTerm(value, termType, datatype));
break;
}
}
@Override
public void visit(RefObjectMap arg)
{
// NO-OP
}
/*
* Private utility methods
*/
private void checkClassSignature(URI uri)
{
if (uri == null) {
return; // if input is null then nothing to check
}
if (isStrictParsing()) {
if (getOntology().containClass(uri)) {
return;
}
throw new SemantikaRuntimeException(format("Class <%s> is not found in ontology", uri));
}
}
private void checkPropertySignature(URI uri)
{
if (uri == null) {
throw new IllegalArgumentException("Property name is null");
}
if (isStrictParsing()) {
if (getOntology().containObjectProperty(uri) || getOntology().containDataProperty(uri)) {
return;
}
throw new SemantikaRuntimeException(format("Property <%s> is not found in ontology", uri));
}
}
private ITerm getColumnTerm(String columnName, String termType, String datatype)
{
if (termType.equals(R2RmlVocabulary.IRI)) {
if (!StringUtils.isEmpty(datatype)) {
throw new IllegalR2RmlMappingException("Cannot use rr:datatype together with term type rr:IRI"); //$NON-NLS-1$
}
SqlColumn column = getColumnTerm(columnName);
column.setTermType(TermType.URI_TYPE);
return column;
}
else if (termType.equals(R2RmlVocabulary.LITERAL)) {
SqlColumn column = getColumnTerm(columnName);
column.setTermType(TermType.LITERAL_TYPE);
if (!StringUtils.isEmpty(datatype)) {
overrideColumn(column, datatype); // set as datatype-override RDF literal
}
return column;
}
else if (termType.equals(R2RmlVocabulary.BLANK_NODE)) {
throw new UnsupportedR2RmlFeatureException("rr:BlankNode as term type"); //$NON-NLS-1$
}
throw new R2RmlParserException(format("Unknown term type \"%s\"", termType)); //$NON-NLS-1$
}
private ITerm getLiteralTerm(String value, String termType, String datatype)
{
if (termType.equals(R2RmlVocabulary.IRI)) {
if (!StringUtils.isEmpty(datatype)) {
throw new IllegalR2RmlMappingException("Cannot use rr:datatype together with term type rr:IRI"); //$NON-NLS-1$
}
return sExpressionObjectFactory.getUriReference(createUri(value));
}
else if (termType.equals(R2RmlVocabulary.LITERAL)) {
return (StringUtils.isEmpty(datatype)) ?
sExpressionObjectFactory.getLiteral(value, DataType.STRING) : // by default
sExpressionObjectFactory.getLiteral(value, datatype);
}
else if (termType.equals(R2RmlVocabulary.BLANK_NODE)) {
throw new UnsupportedR2RmlFeatureException("rr:BlankNode as term type"); //$NON-NLS-1$
}
throw new R2RmlParserException(format("Unknown term type \"%s\"", termType)); //$NON-NLS-1$
}
private ITerm getTemplateTerm(String value, String termType, String datatype)
{
if (termType.equals(R2RmlVocabulary.IRI)) {
if (!StringUtils.isEmpty(datatype)) {
throw new IllegalR2RmlMappingException("Cannot use rr:datatype together with term type rr:IRI"); //$NON-NLS-1$
}
R2RmlTemplate template = new R2RmlTemplate(value);
String templateString = template.getTemplateString();
List<SqlColumn> parameters = getColumnTerms(template.getColumnNames());
UriTemplate uriTemplate = sMappingObjectFactory.createUriTemplate(templateString, parameters);
return uriTemplate;
}
else if (termType.equals(R2RmlVocabulary.LITERAL)) {
throw new UnsupportedR2RmlFeatureException("rr:template for literal string construction");
}
else if (termType.equals(R2RmlVocabulary.BLANK_NODE)) {
throw new UnsupportedR2RmlFeatureException("rr:BlankNode as term type"); //$NON-NLS-1$
}
throw new R2RmlParserException(format("Unknown term type \"%s\"", termType)); //$NON-NLS-1$
}
private SqlColumn getColumnTerm(String columnName)
{
SqlColumn column = (SqlColumn) getSqlQuery().findSelectItemExpression(columnName);
if (column != null) {
return column;
}
throw new R2RmlParserException(format("Unknown column name \"%s\"", columnName)); //$NON-NLS-1$
}
private List<SqlColumn> getColumnTerms(List<String> columnNames)
{
List<SqlColumn> toReturn = new ArrayList<SqlColumn>();
for (String columnName : columnNames) {
toReturn.add(getColumnTerm(columnName));
}
return toReturn;
}
private void overrideColumn(SqlColumn column, String datatype) throws R2RmlParserException
{
try {
column.overrideDatatype(datatype);
}
catch (UnsupportedDataTypeException e) {
throw new IllegalR2RmlMappingException(e.getMessage());
}
catch (IllegalArgumentException e) {
throw new IllegalR2RmlMappingException(e.getMessage());
}
}
private URI createUri(String uriString)
{
if (!StringUtils.isEmpty(uriString)) {
return URI.create(uriString);
}
return null;
}
private URI createUri(ITerm term)
{
if (term instanceof UriReference) {
return ((UriReference) term).toUri();
}
return null;
}
}