with String_Utilities;
package body Misc_String_Utilities is

    function Is_Blank (This_String : in String) return Boolean is
        --
        Blank : constant Character := ' ';
        Tab : constant Character := Ascii.Ht;
        --
    begin  
        if (This_String = "") then
            return (True);
        end if;
        for Index in This_String'First .. This_String'Last loop
            case (This_String (Index)) is
                when Blank | Tab =>
                    null;
                when others =>
                    return (False);
            end case;
        end loop;  
        return (True);
    end Is_Blank;

    function Is_Continuous (This_String : in String) return Boolean is
    begin
        return (Misc_String_Utilities.Continuous (This_String) = This_String);
    end Is_Continuous;

    function Continuous (This_String : in String) return String is
        --
        Tab : constant Character := Ascii.Ht;
        --
    begin
        return (Misc_String_Utilities.Stripped
                   (Misc_String_Utilities.Stripped
                       (This_String, This_Character => Tab)));
    end Continuous;

    function Is_Padded (This_String : in String) return Boolean is
    begin
        return ((String_Utilities.  
                 Strip_Leading (This_String) /= This_String) or else
                (String_Utilities.  
                 Strip_Trailing (This_String) /= This_String));
    end Is_Padded;

    function Strip (Character_At : in Integer; In_String : in String)
                   return String is
        --
        Preceeding_Stuff : constant String :=
           In_String (In_String'First .. (Character_At - 1));
        Following_Stuff : constant String :=
           In_String ((Character_At + 1) .. In_String'Last);  
        New_String_Raw : constant String := Preceeding_Stuff & Following_Stuff;
        New_String_Normalized :
           constant String (1 .. New_String_Raw'Length) := New_String_Raw;
        --
    begin
        return (New_String_Normalized);
    end Strip;

    function Are_Equal (This_Character : in Character;
                        That_Character : in Character;
                        Ignore_Case : in Boolean) return Boolean is
    begin
        if (Ignore_Case) then
            return (String_Utilities.Upper_Case (This_Character) =
                    String_Utilities.Upper_Case (That_Character));
        else  
            return (This_Character = That_Character);
        end if;
    end Are_Equal;

    function Stripped (This_String : in String;
                       This_Character : in Character := ' ';
                       Ignore_Case : in Boolean := False;
                       Starting_At : in Positive := 1) return String is
        --
        The_String : constant String (1 .. This_String'Length) := This_String;
        --
    begin
        if (The_String = "") then
            return (The_String);
        end if;
        for Index in The_String'Range loop
            if (Are_Equal  
                   (The_String (Index), This_Character, Ignore_Case)) then
                return (Stripped (This_String =>
                                     Strip (Character_At => Index,
                                            In_String => The_String),
                                  This_Character => This_Character,
                                  Ignore_Case => Ignore_Case,
                                  Starting_At => Index + 1));
            end if;
        end loop;
        return (The_String);
    end Stripped;

    function Strip (From_Here : in Integer;
                    To_Here : in Integer;
                    In_String : in String) return String is
        --
        Preceeding_Stuff : constant String :=
           In_String (In_String'First .. (From_Here - 1));
        Following_Stuff : constant String :=
           In_String ((To_Here + 1) .. In_String'Last);  
        New_String_Raw : constant String := Preceeding_Stuff & Following_Stuff;
        New_String_Normalized :
           constant String (1 .. New_String_Raw'Length) := New_String_Raw;
        --
    begin
        return (New_String_Normalized);
    end Strip;

    function Are_Equal (This_String : in String;
                        That_String : in String;
                        Ignore_Case : in Boolean) return Boolean is
    begin
        if (Ignore_Case) then
            return (String_Utilities.Upper_Case (This_String) =
                    String_Utilities.Upper_Case (That_String));
        else  
            return (This_String = That_String);
        end if;
    end Are_Equal;

    function Locate (This_Substring : in String;
                     Within_String : in String;
                     Ignore_Case : in Boolean;
                     Starting_At : in Positive := 1) return Natural is
        --
        The_String : constant String (1 .. Within_String'Length) :=
           Within_String;
        --
    begin  
        for Index in Starting_At .. The_String'Last loop
            if (Are_Equal
                   (This_Substring,
                    The_String (Index .. (Index + (This_Substring'Length - 1))),
                    Ignore_Case)) then
                return (Index);
            end if;
        end loop;
        return (0);
        --
    exception
        when others =>
            return (0);
    end Locate;

    function Stripped (This_String : in String;
                       This_Substring : in String;
                       Ignore_Case : in Boolean := False;
                       Starting_At : in Positive := 1) return String is
        --
        The_String : constant String (1 .. This_String'Length) := This_String;
        --
        Current_Index : Natural := 0;
        --
    begin
        if ((This_String = "") or else (This_Substring = "")) then
            -- Is a no-op.
            return (This_String);
        end if;
        loop
            Current_Index := Locate (This_Substring => This_Substring,
                                     Within_String => The_String,
                                     Ignore_Case => Ignore_Case,
                                     Starting_At => Starting_At);
            if (Current_Index /= 0) then
                return (Stripped
                           (This_String =>
                               Strip (From_Here => Current_Index,
                                      To_Here => Current_Index +
                                                    (This_Substring'Length - 1),
                                      In_String => The_String),
                            This_Substring => This_Substring,
                            Ignore_Case => Ignore_Case,
                            Starting_At => Current_Index +
                                              This_Substring'Length));
            else
                return (The_String);
            end if;
        end loop;
    end Stripped;

    function Replace (Character_At : in Integer;
                      With_Character : in Character;
                      In_String : in String) return String is
        --
        Preceeding_Stuff : constant String :=
           In_String (In_String'First .. (Character_At - 1));
        Following_Stuff : constant String :=
           In_String ((Character_At + 1) .. In_String'Last);  
        New_String_Raw : constant String :=
           Preceeding_Stuff & With_Character & Following_Stuff;
        New_String_Normalized :
           constant String (1 .. New_String_Raw'Length) := New_String_Raw;
        --
    begin
        return (New_String_Normalized);
    end Replace;

    function Replaced (This_String : in String;  
                       Old_Character : in Character := '_';
                       New_Character : in Character := ' ';
                       Ignore_Case : in Boolean := False;
                       Starting_At : in Positive := 1) return String is
        --
        The_String : constant String (1 .. This_String'Length) := This_String;
        --

    begin
        if (The_String = "") then
            return (The_String);
        end if;
        for Index in The_String'Range loop
            if (Are_Equal  
                   (The_String (Index), Old_Character, Ignore_Case)) then
                return (Replaced (This_String =>
                                     Replace (Character_At => Index,
                                              With_Character => New_Character,
                                              In_String => The_String),
                                  Old_Character => Old_Character,
                                  New_Character => New_Character,
                                  Ignore_Case => Ignore_Case,
                                  Starting_At => Index + 1));
            end if;
        end loop;
        return (The_String);
    end Replaced;

    function Replace (From_Here : in Integer;
                      To_Here : in Integer;
                      With_Substring : in String;
                      In_String : in String) return String is
        --
        Preceeding_Stuff : constant String :=
           In_String (In_String'First .. (From_Here - 1));
        Following_Stuff : constant String :=
           In_String ((To_Here + 1) .. In_String'Last);  
        New_String_Raw : constant String :=
           Preceeding_Stuff & With_Substring & Following_Stuff;
        New_String_Normalized :
           constant String (1 .. New_String_Raw'Length) := New_String_Raw;
        --
    begin
        return (New_String_Normalized);
    end Replace;

    function Replaced (This_String : in String;
                       Old_Substring : in String;
                       New_Substring : in String;
                       Ignore_Case : in Boolean := False;
                       Starting_At : in Positive := 1) return String is
        --
        The_String : constant String (1 .. This_String'Length) := This_String;
        --
        Current_Index : Natural := 0;
        --
    begin
        if ((This_String = "") or else (Old_Substring = "")) then
            -- Is a no-op.
            return (This_String);
        end if;
        loop
            Current_Index := Locate (This_Substring => Old_Substring,
                                     Within_String => The_String,
                                     Ignore_Case => Ignore_Case,
                                     Starting_At => Starting_At);
            if (Current_Index /= 0) then
                return (Replaced (This_String =>
                                     Replace (From_Here => Current_Index,
                                              To_Here =>
                                                 Current_Index +
                                                    (Old_Substring'Length - 1),
                                              With_Substring => New_Substring,
                                              In_String => The_String),
                                  Old_Substring => Old_Substring,
                                  New_Substring => New_Substring,
                                  Ignore_Case => Ignore_Case,
                                  Starting_At =>
                                     Current_Index + New_Substring'Length));
            else
                return (The_String);
            end if;
        end loop;
    end Replaced;

end Misc_String_Utilities;