code: package it.unibz.inf.ontop.iq.node.impl;
  
  import com.google.common.collect.ImmutableSet;
  import com.google.common.collect.Sets;
  import com.google.inject.Inject;
  import it.unibz.inf.ontop.injection.IntermediateQueryFactory;
  import it.unibz.inf.ontop.iq.IQTree;
  import it.unibz.inf.ontop.iq.IQTreeCache;
  import it.unibz.inf.ontop.iq.exception.InvalidIntermediateQueryException;
  import it.unibz.inf.ontop.iq.exception.QueryNodeTransformationException;
  import it.unibz.inf.ontop.iq.node.*;
  import it.unibz.inf.ontop.iq.node.normalization.DistinctNormalizer;
  import it.unibz.inf.ontop.iq.transform.IQTreeExtendedTransformer;
  import it.unibz.inf.ontop.iq.transform.IQTreeVisitingTransformer;
  import it.unibz.inf.ontop.iq.transform.node.HomogeneousQueryNodeTransformer;
  import it.unibz.inf.ontop.iq.visit.IQVisitor;
  import it.unibz.inf.ontop.model.term.*;
  import it.unibz.inf.ontop.substitution.ImmutableSubstitution;
  import it.unibz.inf.ontop.substitution.InjectiveVar2VarSubstitution;
  import it.unibz.inf.ontop.utils.VariableGenerator;
  
  import java.util.Optional;
  
  
  public class DistinctNodeImpl extends QueryModifierNodeImpl implements DistinctNode {
  
      private static final String DISTINCT_NODE_STR = "DISTINCT";
      private final DistinctNormalizer normalizer;
  
      @Inject
      private DistinctNodeImpl(IntermediateQueryFactory iqFactory, DistinctNormalizer normalizer) {
          super(iqFactory);
          this.normalizer = normalizer;
      }
  
      @Override
      public IQTree normalizeForOptimization(IQTree child, VariableGenerator variableGenerator, IQTreeCache treeCache) {
          return normalizer.normalizeForOptimization(this, child, variableGenerator, treeCache);
      }
  
      /**
       * TODO: implement it seriously! (is currently blocking)
       */
      @Override
      public IQTree liftIncompatibleDefinitions(Variable variable, IQTree child, VariableGenerator variableGenerator) {
          // TODO: stop blocking
          return iqFactory.createUnaryIQTree(this, child);
      }
  
      @Override
      public IQTree applyDescendingSubstitution(ImmutableSubstitution<? extends VariableOrGroundTerm> descendingSubstitution,
                                                Optional<ImmutableExpression> constraint, IQTree child,
                                                VariableGenerator variableGenerator) {
          return iqFactory.createUnaryIQTree(this,
                  child.applyDescendingSubstitution(descendingSubstitution, constraint, variableGenerator));
      }
  
      @Override
      public IQTree applyDescendingSubstitutionWithoutOptimizing(
              ImmutableSubstitution<? extends VariableOrGroundTerm> descendingSubstitution, IQTree child,
              VariableGenerator variableGenerator) {
          return iqFactory.createUnaryIQTree(this,
                  child.applyDescendingSubstitutionWithoutOptimizing(descendingSubstitution, variableGenerator));
      }
  
      @Override
      public IQTree applyFreshRenaming(InjectiveVar2VarSubstitution renamingSubstitution, IQTree child, IQTreeCache treeCache) {
          IQTree newChild = child.applyFreshRenaming(renamingSubstitution);
          IQTreeCache newTreeCache = treeCache.applyFreshRenaming(renamingSubstitution);
          return iqFactory.createUnaryIQTree(this, newChild, newTreeCache);
      }
  
      @Override
      public boolean isDistinct(IQTree tree, IQTree child) {
          return true;
      }
  
      @Override
      public IQTree acceptTransformer(IQTree tree, IQTreeVisitingTransformer transformer, IQTree child) {
          return transformer.transformDistinct(tree, this, child);
      }
  
      @Override
      public <T> IQTree acceptTransformer(IQTree tree, IQTreeExtendedTransformer<T> transformer, IQTree child, T context) {
          return transformer.transformDistinct(tree, this, child, context);
      }
  
      @Override
      public <T> T acceptVisitor(IQVisitor<T> visitor, IQTree child) {
          return visitor.visitDistinct(this, child);
      }
  
      @Override
      public void validateNode(IQTree child) throws InvalidIntermediateQueryException {
      }
  
      @Override
      public IQTree removeDistincts(IQTree child, IQTreeCache treeCache) {
          return child.removeDistincts();
      }
  
      @Override
      public ImmutableSet<ImmutableSet<Variable>> inferUniqueConstraints(IQTree child) {
          return Sets.union(
                  child.inferUniqueConstraints(),
                  ImmutableSet.of(child.getVariables())).immutableCopy();
      }
  
      /**
       * TODO: implement it more seriously, by consider functional dependencies between variables
       */
      @Override
      public ImmutableSet<Variable> computeNotInternallyRequiredVariables(IQTree child) {
          return ImmutableSet.of();
      }
  
      @Override
      public void acceptVisitor(QueryNodeVisitor visitor) {
          visitor.visit(this);
      }
  
      @Override
      public DistinctNode acceptNodeTransformer(HomogeneousQueryNodeTransformer transformer) throws QueryNodeTransformationException {
          return transformer.transform(this);
      }
  
      @Override
      public ImmutableSet<Variable> getLocalVariables() {
          return ImmutableSet.of();
      }
  
      @Override
      public ImmutableSet<Variable> getLocallyRequiredVariables() {
          return ImmutableSet.of();
      }
  
      @Override
      public ImmutableSet<Variable> getLocallyDefinedVariables() {
          return ImmutableSet.of();
      }
  
      @Override
      public int hashCode() {
          return 238723871;
      }
  
      @Override
      public boolean equals(Object o) {
          if (this == o) return true;
          return o != null && getClass() == o.getClass();
      }
  
      @Override
      public String toString() {
          return DISTINCT_NODE_STR;
      }
  }
  