code: private IQTree convertAtoms(List<Function> atoms, Collection<Predicate> tablePredicates,
                                  IntermediateQueryFactory iqFactory)
              throws DatalogProgram2QueryConverterImpl.InvalidDatalogProgramException, IntermediateQueryBuilderException {
  
          AtomClassification classification = new AtomClassification(atoms, datalogTools);
  
          Optional<ImmutableExpression> optionalFilterCondition = createFilterExpression(
                  classification.booleanAtoms);
  
          if (classification.dataAndCompositeAtoms.isEmpty()) {
              return optionalFilterCondition
                      .map(ImmutableFunctionalTerm::getVariables)
                      .map(iqFactory::createEmptyNode)
                      .orElseGet(() -> iqFactory.createEmptyNode(ImmutableSet.of()));
          }
          /*
           * May happen because this method can also be called after the LJ conversion
           */
          else if (classification.dataAndCompositeAtoms.length() == 1) {
              if (optionalFilterCondition.isPresent()) {
                  FilterNode filterNode = iqFactory.createFilterNode(optionalFilterCondition.get());
                  IQTree childTree = convertDataOrCompositeAtom(classification.dataAndCompositeAtoms.index(0),
                          tablePredicates, iqFactory);
  
                  return iqFactory.createUnaryIQTree(filterNode, childTree);
              }
              /*
               * Otherwise, no need for intermediate query node.
               */
              else {
                  return convertDataOrCompositeAtom(classification.dataAndCompositeAtoms.index(0), tablePredicates, iqFactory);
              }
          }
          /*
           * Normal case
           */
          else {
              InnerJoinNode joinNode = iqFactory.createInnerJoinNode(optionalFilterCondition);
  
              /*
               * Indirect recursive call for composite atoms
               */
              ImmutableList<IQTree> children = classification.dataAndCompositeAtoms.toJavaList().stream()
                      .map(a -> convertDataOrCompositeAtom(a, tablePredicates, iqFactory))
                      .collect(ImmutableCollectors.toList());
  
              return iqFactory.createNaryIQTree(joinNode, children);
          }
  
      }
docs: TODO: explain