All Downloads are FREE. Search and download functionalities are using the official Maven repository.

it.unibz.inf.ontop.model.term.functionsymbol.impl.SPARQLCastFunctionSymbolImpl Maven / Gradle / Ivy

package it.unibz.inf.ontop.model.term.functionsymbol.impl;

import com.google.common.collect.ImmutableList;
import it.unibz.inf.ontop.iq.node.VariableNullability;
import it.unibz.inf.ontop.model.term.*;
import it.unibz.inf.ontop.model.term.functionsymbol.db.DBFunctionSymbol;
import it.unibz.inf.ontop.model.type.*;
import org.apache.commons.rdf.api.IRI;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeParseException;
import java.util.Optional;
import java.util.function.Function;
import java.util.regex.Pattern;

import static it.unibz.inf.ontop.model.type.DBTermType.Category.*;

public class SPARQLCastFunctionSymbolImpl extends ReduciblePositiveAritySPARQLFunctionSymbolImpl {

    private final RDFTermType targetType;
    private final DBTypeFactory dbTypeFactory;
    private final Pattern numericPattern = Pattern.compile("-?\\d+(\\.\\d+)?");
    private final ImmutableList booleanPattern = ImmutableList.of("true", "false", "t", "f", "1", "0");

    private final Function> dbFunctionSymbolFct;


    public SPARQLCastFunctionSymbolImpl(String functionSymbolName, IRI iriFunctionName, RDFDatatype targetDataType,
                                        TypeFactory typeFactory,
                                        Function> dbFunctionSymbolFct) {
        super(functionSymbolName, iriFunctionName, ImmutableList.of(typeFactory.getAbstractRDFTermType()));
        this.targetType = targetDataType;
        this.dbTypeFactory = typeFactory.getDBTypeFactory();
        this.dbFunctionSymbolFct = dbFunctionSymbolFct;
    }

    @Override
    protected ImmutableTerm computeLexicalTerm(ImmutableList subLexicalTerms,
                                               ImmutableList typeTerms, TermFactory termFactory, ImmutableTerm returnedTypeTerm) {

        DBTermType targetTypeClosestDBType = targetType.getClosestDBType(dbTypeFactory);


        // CASE 1: Input is typed
        if ((typeTerms.size()>0) && (typeTerms.get(0) instanceof RDFTermTypeConstant)) {
            RDFTermTypeConstant inputRDFType = (RDFTermTypeConstant) typeTerms.get(0);

            // If no cast necessary, return term
            if (inputRDFType.equals(returnedTypeTerm)) {
                return subLexicalTerms.get(0);
            }

            // If constant is not consistent with expected output type, return NULL
            if (!(checkTermConversionConsistency(inputRDFType.getRDFTermType(), termFactory,
                    targetTypeClosestDBType, subLexicalTerms.get(0)))) {
                return termFactory.getNullConstant();
            }

            DBTermType inputDBType = inputRDFType.getRDFTermType().getClosestDBType(dbTypeFactory);
            return dbFunctionSymbolFct.apply(inputDBType).isPresent()
                    ? termFactory.getImmutableFunctionalTerm(dbFunctionSymbolFct.apply(inputDBType).get(), subLexicalTerms.get(0))
                    : termFactory.getNullConstant();
        } else {
            // CASE 2: Input is not typed or variable, use STRING as default
            return termFactory.getImmutableFunctionalTerm(
                    dbFunctionSymbolFct.apply(dbTypeFactory.getDBStringType()).get(), subLexicalTerms.get(0));
        }
    }

    @Override
    protected ImmutableTerm computeTypeTerm(ImmutableList subLexicalTerms,
                                            ImmutableList typeTerms, TermFactory termFactory,
                                            VariableNullability variableNullability) {
        return termFactory.getRDFTermTypeConstant(targetType);
    }

    @Override
    public boolean isAlwaysInjectiveInTheAbsenceOfNonInjectiveFunctionalTerms() {
        return false;
    }

    @Override
    public Optional inferType(ImmutableList terms) {
        return Optional.of(TermTypeInference.declareTermType(targetType));
    }

    @Override
    public boolean canBePostProcessed(ImmutableList arguments) {
        return false;
    }

    //TODO: Ideally this logic should pushed in the lower-level function symbols, that operates over DB terms.
    // This would maximize the chances for this logic to be used, because we may not have yet constants when reducing to
    // the SPARQL function into DB functions.
    /**
     * Checks for datatype compatibility, otherwise returns NULL
     * CASE 1: Conversion between datatypes not permitted e.g. integer to datetime
     * CASE 2: Conversion of particular values not permitted e.g. string "123a" to integer
     */
    protected boolean checkTermConversionConsistency(RDFTermType rdfTermType, TermFactory termFactory,
                                                     DBTermType targetType2, ImmutableTerm term) {

        DBTermType.Category inputType = rdfTermType.getClosestDBType(termFactory.getTypeFactory().getDBTypeFactory())
                .getCategory();

        if (targetType2.getNaturalRDFDatatype().isPresent()) {
            switch (targetType2.getCategory()) {
                case DATE:
                    // Case 1: If concrete numeric, no conversion
                    if (rdfTermType instanceof ConcreteNumericRDFDatatype)
                        return false;
                    if (inputType.equals(STRING) && (term instanceof DBConstant)) {
                        try {
                            LocalDate.parse(((DBConstant) term).getValue());
                            return true;
                        } catch (DateTimeParseException e) {
                            return false;
                        }
                    }
                    break;
                case DATETIME:
                    if (rdfTermType instanceof ConcreteNumericRDFDatatype)
                        return false;
                    if (inputType.equals(STRING) && (term instanceof DBConstant)) {
                        try {
                            // Drop Z from datetime format
                            String targetDate = ((DBConstant) term).getValue().replace("Z", "");
                            if (targetDate.chars().filter(ch -> ch == ':').count() == 3) {
                                // Drop timezone; check only if valid datetime
                                LocalDateTime.parse(targetDate.substring(0, targetDate.length()-6));
                                // Check if valid timezone
                                // Imperfect solution since some timezone combinations might not exist in the real world
                                return (ImmutableList.of("00", "30", "45").contains(targetDate.substring(targetDate.length() - 2)))
                                        && (ImmutableList.of("-", "+").contains(String.valueOf(targetDate.charAt(targetDate.length() - 6))))
                                        && (Integer.parseInt(targetDate.substring(targetDate.length() - 5, targetDate.length() - 3)) < 15);
                            } else {
                                // Check if valid datetime
                                LocalDateTime.parse(targetDate);
                                return true;
                            }

                        } catch (DateTimeParseException e) {
                            return false;
                        }
                    }
                    break;
                case FLOAT_DOUBLE:
                case DECIMAL:
                case INTEGER:
                    if (inputType.equals(DATE) || inputType.equals(DATETIME))
                        return false;
                    if (inputType.equals(STRING) && (term instanceof DBConstant)) {
                        if (term.isNull()) { return false; }
                        return numericPattern.matcher(((DBConstant) term).getValue()).matches();
                    }
                    break;
                case BOOLEAN:
                    if (inputType.equals(DATE) || inputType.equals(DATETIME))
                        return false;
                    if (inputType.equals(STRING) && (term instanceof DBConstant)) {
                        if (term.isNull()) { return false; }
                        return booleanPattern.contains(((DBConstant) term).getValue());
                    }
                    break;
            }
        }
        return true;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy