/** * Normalizes the input substitution so as to avoid projected variables to be renamed into variables * that are NOT PROJECTED by the closest ExplicitVariableProjectionNode. * * Such an ancestor BLOCKS ascending substitutions and may thus be forced to incorporate some bindings * of the substitution (if it is a ConstructionNode) or to insert a construction node out of a fragment * of the substitution (if it is a UnionNode). * * Without this normalization, blocking the substitution could cause the insertion of illegal bindings * (projected variable to non-projected variable) into a construction node, producing an invalid IQ. * * An alternative solution to normalization would have been to let the ancestors push down a renaming * substitution, but this complicates the substitution propagation mechanisms. * */ private ImmutableSubstitution<? extends ImmutableTerm> normalizeInputSubstitution( N originalFocusNode, IntermediateQuery query, ImmutableSubstitution<? extends ImmutableTerm> substitution) { if (query.getRootNode() == originalFocusNode) return substitution; ImmutableSet<Variable> priorityVariables = query.getAncestors(originalFocusNode).stream() .filter(a -> a instanceof ExplicitVariableProjectionNode) .map(a -> (ExplicitVariableProjectionNode) a) .findFirst() .map(ExplicitVariableProjectionNode::getVariables) .orElseGet(() -> query.getProjectionAtom().getVariables()); return ImmutableSubstitutionTools.prioritizeRenaming(substitution, priorityVariables); }