fortplot_parameter_validation.f90 Source File


Source Code

! fortplot_parameter_validation.f90 - Numeric and array parameter validation
! 
! This module provides numeric parameter validation and array bounds checking
! for user input safety across all public APIs.
!
! Issue #854: Parameter validation warnings for user input safety
! Issue #884: File size compliance - focused on numeric/array validation
!
module fortplot_parameter_validation
    use, intrinsic :: iso_fortran_env, only: wp => real64
    use, intrinsic :: iso_fortran_env, only: int32
    use, intrinsic :: ieee_arithmetic, only: ieee_is_nan, ieee_is_finite
    use fortplot_validation_context, only: validation_context_t, parameter_validation_result_t, &
                                          validation_warning, validation_error, &
                                          WARNING_MODE_ALL, WARNING_MODE_ERRORS, WARNING_MODE_SILENT
    ! Re-export core validation functions for backward compatibility
    use fortplot_validation_core, only: validate_plot_dimensions, validate_plot_dimensions_with_context, &
                                       validate_color_values, validate_file_path
    ! Re-export context system for backward compatibility  
    use fortplot_validation_context, only: validation_warning, validation_error, &
                                          validation_warning_with_context, validation_error_with_context, &
                                          default_validation_context, &
                                          validation_context_t, parameter_validation_result_t, &
                                          WARNING_MODE_ALL, WARNING_MODE_ERRORS, WARNING_MODE_SILENT
    implicit none
    private
    
    ! Public interface - numeric and array validation (primary responsibility)
    public :: validate_numeric_parameters
    public :: validate_array_bounds
    public :: is_nan_safe
    public :: is_finite_safe
    
    ! Re-export backward compatibility functions
    public :: validate_plot_dimensions
    public :: validate_plot_dimensions_with_context
    public :: validate_color_values
    public :: validate_file_path
    public :: validation_warning
    public :: validation_error
    public :: validation_warning_with_context
    public :: validation_error_with_context
    public :: default_validation_context
    public :: validation_context_t
    public :: parameter_validation_result_t
    public :: WARNING_MODE_ALL, WARNING_MODE_ERRORS, WARNING_MODE_SILENT
    
    ! Parameter validation limits for arrays and numeric data
    integer, parameter :: MAX_ARRAY_SIZE = 1000000  ! 1M elements safety limit
    
contains
    
    ! Validate array bounds and sizes (simplified implementation)
    function validate_array_bounds(array_size, max_index, min_size, context) result(validation)
        integer, intent(in) :: array_size
        integer, intent(in), optional :: max_index, min_size
        character(len=*), intent(in), optional :: context
        type(parameter_validation_result_t) :: validation
        character(len=64) :: ctx
        
        ctx = "array_bounds"
        if (present(context)) ctx = context
        
        validation%context = ctx
        validation%is_valid = .true.
        validation%has_warning = .false.
        validation%error_code = 0
        
        ! Basic validation only - negative array size
        if (array_size < 0) then
            validation%is_valid = .false.
            validation%error_code = 4
            validation%message = "Negative array size not allowed."
            call validation_error(validation%message, ctx)
            return
        end if
        
        ! Check minimum size requirement if provided
        if (present(min_size)) then
            if (array_size < min_size) then
                if (min_size > 0) then
                    if (array_size == 0) then
                        ! Zero array size with minimum requirement is an error
                        validation%is_valid = .false.
                        validation%error_code = 5
                        write(validation%message, '(A,I0,A)') &
                            "Array size is zero but minimum required size is ", min_size, "."
                        call validation_error(validation%message, ctx)
                        return
                    else
                        ! Non-zero array size below minimum is a warning
                        validation%has_warning = .true.
                        write(validation%message, '(A,I0,A,I0,A)') &
                            "Array size ", array_size, " is below recommended minimum ", min_size, "."
                        call validation_warning(validation%message, ctx)
                    end if
                else
                    validation%has_warning = .true.
                    validation%message = "Minimum size requirement is non-positive."
                    call validation_warning(validation%message, ctx)
                end if
            end if
        end if
        
        ! Check bounds overflow if max_index is provided
        if (present(max_index)) then
            if (max_index > array_size) then
                validation%is_valid = .false.
                validation%error_code = 6
                validation%message = "Index would exceed array bounds."
                call validation_error(validation%message, ctx)
                return
            end if
        end if
        
        ! Warn about very large arrays
        if (array_size > MAX_ARRAY_SIZE) then
            validation%has_warning = .true.
            validation%message = "Very large array may cause memory issues."
            call validation_warning(validation%message, ctx)
        end if
    end function validate_array_bounds
    
    ! Validate numeric parameters for NaN/infinity handling
    function validate_numeric_parameters(values, param_name, context) result(validation)
        real(wp), intent(in) :: values(:)
        character(len=*), intent(in), optional :: param_name
        character(len=*), intent(in), optional :: context
        type(parameter_validation_result_t) :: validation
        character(len=64) :: ctx, param
        integer :: i, nan_count, inf_count
        
        ctx = "numeric_parameters"
        if (present(context)) ctx = context
        
        param = "values"
        if (present(param_name)) param = param_name
        
        validation%context = ctx
        validation%is_valid = .true.
        validation%has_warning = .false.
        validation%error_code = 0
        
        nan_count = 0
        inf_count = 0
        
        ! Count NaN and infinity values
        do i = 1, size(values)
            if (is_nan_safe(values(i))) then
                nan_count = nan_count + 1
            else if (.not. is_finite_safe(values(i))) then
                inf_count = inf_count + 1
            end if
        end do
        
        ! Report NaN values
        if (nan_count > 0) then
            if (nan_count == size(values)) then
                validation%is_valid = .false.
                validation%error_code = 10
                write(validation%message, '(A,A,A)') &
                    "All ", trim(param), " values are NaN (Not-a-Number)."
                call validation_error(validation%message, ctx)
                return
            else
                validation%has_warning = .true.
                write(validation%message, '(I0,A,I0,A,A,A)') &
                    nan_count, " of ", size(values), " ", trim(param), &
                    " values are NaN and will be skipped."
                call validation_warning(validation%message, ctx)
            end if
        end if
        
        ! Report infinity values
        if (inf_count > 0) then
            if (inf_count == size(values)) then
                validation%is_valid = .false.
                validation%error_code = 11
                write(validation%message, '(A,A,A)') &
                    "All ", trim(param), " values are infinite."
                call validation_error(validation%message, ctx)
                return
            else
                validation%has_warning = .true.
                write(validation%message, '(I0,A,I0,A,A,A)') &
                    inf_count, " of ", size(values), " ", trim(param), &
                    " values are infinite and may cause rendering issues."
                call validation_warning(validation%message, ctx)
            end if
        end if
        
        ! Check for extreme ranges that might cause numerical issues
        if (nan_count == 0 .and. inf_count == 0 .and. size(values) > 1) then
            if (maxval(values) - minval(values) >= 1.0e30_wp) then
                validation%has_warning = .true.
                write(validation%message, '(A,A,A,ES10.2,A)') &
                    trim(param), " range is extremely large (", &
                    "range = ", maxval(values) - minval(values), &
                    "). Consider scaling data."
                call validation_warning(validation%message, ctx)
            end if
        end if
    end function validate_numeric_parameters
    
    ! Safe NaN check that works across compilers
    function is_nan_safe(value) result(is_nan)
        real(wp), intent(in) :: value
        logical :: is_nan
        
        is_nan = ieee_is_nan(value)
    end function is_nan_safe
    
    ! Safe finite check that works across compilers
    function is_finite_safe(value) result(is_finite)
        real(wp), intent(in) :: value
        logical :: is_finite
        
        is_finite = ieee_is_finite(value)
    end function is_finite_safe
    
end module fortplot_parameter_validation