code: private static Operator substitute(Operator oper, List[] equatedIds) {
      Selector op1 = oper.getOperands()[0];
      Selector op2 = (oper.getOperands().length == 1) ? null : oper.getOperands()[1];
      if (op1 instanceof Identifier)
        op1 = substitute((Identifier) op1, equatedIds);
      else if (op1 instanceof Operator)
        op1 = substitute((Operator) op1, equatedIds);
      if (op1 == null)
        return null;
      if (op2 != null) {
        if (op2 instanceof Identifier)
          op2 = substitute((Identifier) op2, equatedIds);
        else if (op2 instanceof Operator)
          op2 = substitute((Operator) op2, equatedIds);
        if (op2 == null)
          return null;
      }
      if (op1 == oper.getOperands()[0] && (op2 == null || op2 == oper.getOperands()[1]))
        return oper;
      else if (oper instanceof LikeOperator) {
        LikeOperatorImpl loper = (LikeOperatorImpl) oper;
          return new LikeOperatorImpl(loper.getOp(), op1, loper.getInternalPattern(), loper.getPattern(), loper.isEscaped(), loper.getEscape());
      }
      else
        return (op2 == null) ? new OperatorImpl(oper.getOp(), op1) : new OperatorImpl(oper.getOp(), op1, op2);
    }
docs: mutations are made directly to any tree nodes.