code: package it.unibz.inf.ontop.model.term.functionsymbol.db.impl;
  
  import com.google.common.collect.ImmutableList;
  import io.mikael.urlbuilder.util.Decoder;
  import io.mikael.urlbuilder.util.Encoder;
  import it.unibz.inf.ontop.iq.node.VariableNullability;
  import it.unibz.inf.ontop.model.term.*;
  import it.unibz.inf.ontop.model.term.functionsymbol.FunctionSymbol;
  import it.unibz.inf.ontop.model.term.functionsymbol.db.DBTypeConversionFunctionSymbol;
  import it.unibz.inf.ontop.model.term.functionsymbol.db.IRISafenessDeclarationFunctionSymbol;
  import it.unibz.inf.ontop.model.type.DBTermType;
  import it.unibz.inf.ontop.utils.R2RMLIRISafeEncoder;
  
  import java.nio.charset.Charset;
  import java.nio.charset.StandardCharsets;
  
  public abstract class AbstractEncodeURIorIRIFunctionSymbol extends AbstractTypedDBFunctionSymbol {
  
      private final EnDecoder enDecoder;
  
      protected AbstractEncodeURIorIRIFunctionSymbol(DBTermType dbStringType, boolean preserveInternationalChars) {
          super(preserveInternationalChars ? "R2RMLIRISafeEncode" : "DB_ENCODE_FOR_URI", ImmutableList.of(dbStringType), dbStringType);
          this.enDecoder = preserveInternationalChars
                  ? new IRISafeEnDecoder()
                  : new EnDecoderForURI();
      }
  
      @Override
      protected ImmutableTerm buildTermAfterEvaluation(ImmutableList<ImmutableTerm> newTerms,
                                                       TermFactory termFactory, VariableNullability variableNullability) {
          ImmutableTerm newTerm = newTerms.get(0);
          if (newTerm instanceof DBConstant)
              return encodeConstant((DBConstant) newTerm, termFactory);
  
          /*
           * Looks for DB type conversions (e.g. casts)
           *  - from a DBTermType that is known to be safe (e.g. decimals)
           *  - from an argument that is declared as safe
           */
          if (newTerm instanceof ImmutableFunctionalTerm) {
              FunctionSymbol functionSymbol = ((ImmutableFunctionalTerm) newTerm).getFunctionSymbol();
  
              if (functionSymbol instanceof DBTypeConversionFunctionSymbol) {
                  DBTypeConversionFunctionSymbol conversionFunctionSymbol = (DBTypeConversionFunctionSymbol) functionSymbol;
                  if (conversionFunctionSymbol.getInputType()
                          .filter(t -> !t.isNeedingIRISafeEncoding())
                          .isPresent())
                      return newTerm;
  
                  if (conversionFunctionSymbol.isSimple()) {
                      ImmutableTerm grandChildTerm = ((ImmutableFunctionalTerm) newTerm).getTerm(0);
                      if (grandChildTerm instanceof ImmutableFunctionalTerm) {
                          ImmutableFunctionalTerm grandChildFunctionalTerm = (ImmutableFunctionalTerm) grandChildTerm;
                          if (grandChildFunctionalTerm.getFunctionSymbol() instanceof IRISafenessDeclarationFunctionSymbol)
                              return termFactory.getImmutableFunctionalTerm(
                                      conversionFunctionSymbol,
                                      ((ImmutableFunctionalTerm) grandChildTerm).getTerm(0));
                      }
                  }
              }
  
              if (functionSymbol instanceof IRISafenessDeclarationFunctionSymbol) {
                  return ((ImmutableFunctionalTerm) newTerm).getTerm(0);
              }
          }
  
          return super.buildTermAfterEvaluation(newTerms, termFactory, variableNullability);
      }
  
      private DBConstant encodeConstant(DBConstant constant, TermFactory termFactory) {
          return termFactory.getDBStringConstant(enDecoder.encode(constant.getValue()));
      }
  
      @Override
      protected boolean isAlwaysInjectiveInTheAbsenceOfNonInjectiveFunctionalTerms() {
          return true;
      }
  
      @Override
      protected IncrementalEvaluation evaluateStrictEqWithNonNullConstant(ImmutableList<? extends ImmutableTerm> terms,
                                                                          NonNullConstant otherTerm, TermFactory termFactory,
                                                                          VariableNullability variableNullability) {
          DBConstant decodedConstant = termFactory.getDBStringConstant(
                  enDecoder.decode(otherTerm.getValue()));
  
          ImmutableExpression newExpression = termFactory.getStrictEquality(terms.get(0), decodedConstant);
  
          return newExpression.evaluate(variableNullability, true);
      }
  
      interface EnDecoder {
          String encode(String value);
          String decode(String value);
      }
  
      /**
       * Not preserving international characters
       */
      protected static class EnDecoderForURI implements EnDecoder {
  
          private final Encoder uriEncoder;
          private final Decoder uriDecoder;
  
          protected EnDecoderForURI() {
              Charset charset = StandardCharsets.UTF_8;
              this.uriEncoder = new Encoder(charset);
              this.uriDecoder = new Decoder(charset);
          }
  
          /**
           * Query element: percent-encoding except if in iunreserved
           */
          @Override
          public String encode(String value) {
              return uriEncoder.encodeQueryElement(value);
          }
  
          @Override
          public String decode(String value) {
              return uriDecoder.urlDecode(value, true);
          }
      }
  
      /**
       * reserving international characters
       */
      protected static class IRISafeEnDecoder implements EnDecoder {
  
          protected IRISafeEnDecoder() {
          }
  
          @Override
          public String encode(String value) {
              return R2RMLIRISafeEncoder.encode(value);
          }
  
          @Override
          public String decode(String value) {
              return R2RMLIRISafeEncoder.decode(value);
          }
      }
  
  
  }
  