<commit_before>-----------------------------------------------------------------------
--  html.forms -- ASF HTML Form Components
--  Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016 <NAME>
--  Written by <NAME> (<EMAIL>)
--
--  Licensed under the Apache License, Version 2.0 (the "License");
--  you may not use this file except in compliance with the License.
--  You may obtain a copy of the License at
--
--      http://www.apache.org/licenses/LICENSE-2.0
--
--  Unless required by applicable law or agreed to in writing, software
--  distributed under the License is distributed on an "AS IS" BASIS,
--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--  See the License for the specific language governing permissions and
--  limitations under the License.
-----------------------------------------------------------------------

with Ada.Unchecked_Deallocation;

with Util.Beans.Objects;
with Util.Log.Loggers;
with Ada.Exceptions;
with ASF.Utils;
with ASF.Parts;
with ASF.Parts.Upload_Method;
with ASF.Converters;
with ASF.Requests;
with ASF.Components.Base;
with ASF.Components.Utils;
with ASF.Components.Root;
with ASF.Events.Faces.Actions;
with ASF.Applications.Main;
with ASF.Applications.Views;
package body ASF.Components.Html.Forms is

   --  The logger
   Log : constant Util.Log.Loggers.Logger := Util.Log.Loggers.Create ("ASF.Components.Html.Forms");

   FORM_ATTRIBUTE_NAMES      : Util.Strings.String_Set.Set;

   INPUT_ATTRIBUTE_NAMES     : Util.Strings.String_Set.Set;

   TEXTAREA_ATTRIBUTE_NAMES  : Util.Strings.String_Set.Set;

   FILE_ATTRIBUTE_NAMES      : Util.Strings.String_Set.Set;

   procedure Free is
      new Ada.Unchecked_Deallocation (Object => ASF.Validators.Validator'Class,
                                      Name   => ASF.Validators.Validator_Access);

   --  ------------------------------
   --  Input Component
   --  ------------------------------

   --  ------------------------------
   --  Find the form component which contains the input component.
   --  Returns null if the input is not within a form component.
   --  ------------------------------
   function Get_Form (UI : in UIInput) return UIForm_Access is
      use type ASF.Components.Base.UIComponent_Access;

      Parent : ASF.Components.Base.UIComponent_Access := UI.Get_Parent;
   begin
      while Parent /= null loop
         if Parent.all in UIForm'Class then
            return UIForm'Class (Parent.all)'Access;
         end if;
         Parent := Parent.Get_Parent;
      end loop;
      return null;
   end Get_Form;

   --  ------------------------------
   --  Check if this component has the required attribute set.
   --  ------------------------------
   function Is_Required (UI      : in UIInput;
                         Context : in Faces_Context'Class) return Boolean is
      Attr : constant EL.Objects.Object := UI.Get_Attribute (Name    => REQUIRED_NAME,
                                                             Context => Context);
   begin
      if EL.Objects.Is_Null (Attr) then
         return True;
      end if;
      return EL.Objects.To_Boolean (Attr);
   end Is_Required;

   --  ------------------------------
   --  Get the value of the component.  If the component has a submitted value, returns it.
   --  If the component has a local value which is not null, returns it.
   --  Otherwise, if we have a Value_Expression evaluate and returns the value.
   --  ------------------------------
   overriding
   function Get_Value (UI : in UIInput) return EL.Objects.Object is
   begin
      if not Util.Beans.Objects.Is_Null (UI.Submitted_Value) then
         return UI.Submitted_Value;
      else
         return Text.UIOutput (UI).Get_Value;
      end if;
   end Get_Value;

   --  ------------------------------
   --  Set the input component as a password field.
   --  ------------------------------
   procedure Set_Secret (UI    : in out UIInput;
                         Value : in Boolean) is
   begin
      UI.Is_Secret := Value;
   end Set_Secret;

   --  ------------------------------
   --  Render the input element.
   --  ------------------------------
   procedure Render_Input (UI       : in UIInput;
                           Context  : in out Faces_Context'Class;
                           Write_Id : in Boolean := True) is
      Writer : constant Response_Writer_Access := Context.Get_Response_Writer;
      Value  : constant EL.Objects.Object := UIInput'Class (UI).Get_Value;
   begin
      Writer.Start_Element ("input");
      if UI.Is_Secret then
         Writer.Write_Attribute (Name => "type", Value => "password");
      else
         Writer.Write_Attribute (Name => "type", Value => "text");
      end if;
      Writer.Write_Attribute (Name => "name", Value => UI.Get_Client_Id);
      if not EL.Objects.Is_Null (Value) then
         declare
            Convert : constant access Converters.Converter'Class
              := UIInput'Class (UI).Get_Converter;
         begin
            if Convert /= null and Util.Beans.Objects.Is_Null (UI.Submitted_Value) then
               Writer.Write_Attribute (Name  => "value",
                                       Value => Convert.To_String (Value => Value,
                                                                   Component => UI,
                                                                   Context => Context));
            else
               Writer.Write_Attribute (Name => "value", Value => Value);
            end if;
         end;
      end if;
      UI.Render_Attributes (Context, INPUT_ATTRIBUTE_NAMES, Writer, Write_Id);
      Writer.End_Element ("input");
   end Render_Input;

   overriding
   procedure Encode_Begin (UI      : in UIInput;
                           Context : in out Faces_Context'Class) is
   begin
      if UI.Is_Rendered (Context) then
         UIInput'Class (UI).Render_Input (Context);
      end if;
   end Encode_Begin;

   --  ------------------------------
   --  Get the input parameter from the submitted context.  This operation is called by
   --  <tt>Process_Decodes</tt> to retrieve the request parameter associated with the component.
   --  ------------------------------
   function Get_Parameter (UI      : in UIInput;
                           Context : in Faces_Context'Class) return String is
      Id  : constant Unbounded_String := UI.Get_Client_Id;
   begin
      return Context.Get_Parameter (To_String (Id));
   end Get_Parameter;

   overriding
   procedure Process_Decodes (UI      : in out UIInput;
                              Context : in out Faces_Context'Class) is
   begin
      if not UI.Is_Rendered (Context) then
         return;
      end if;
      declare
         Id  : constant Unbounded_String := UI.Get_Client_Id;
         Val : constant String := UIInput'Class (UI).Get_Parameter (Context);
      begin
         if not UI.Is_Secret then
            Log.Debug ("Set input parameter {0} -> {1}", Id, Val);
         end if;
         UI.Submitted_Value := UIInput'Class (UI).Convert_Value (Val, Context);
         UI.Is_Valid := True;

      exception
         when E : others =>
            UI.Is_Valid := False;
            Log.Info (Utils.Get_Line_Info (UI)
                      & ": Exception raised when converting value {0} for component {1}: {2}",
                      Val, To_String (Id), Ada.Exceptions.Exception_Name (E));
      end;
   end Process_Decodes;

   --  ------------------------------
   --  Perform the component tree processing required by the <b>Process Validations</b>
   --  phase of the request processing lifecycle for all facets of this component,
   --  all children of this component, and this component itself, as follows:
   --  <ul><commit_msg>Fix the handling of values not submitted.  Since AWS does not tell us whether a parameter exists or not, ignore the parameter when it is not required and we find an empty value<commit_after>-----------------------------------------------------------------------
--  html.forms -- ASF HTML Form Components
--  Copyright (C) 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 <NAME>
--  Written by <NAME> (<EMAIL>)
--
--  Licensed under the Apache License, Version 2.0 (the "License");
--  you may not use this file except in compliance with the License.
--  You may obtain a copy of the License at
--
--      http://www.apache.org/licenses/LICENSE-2.0
--
--  Unless required by applicable law or agreed to in writing, software
--  distributed under the License is distributed on an "AS IS" BASIS,
--  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
--  See the License for the specific language governing permissions and
--  limitations under the License.
-----------------------------------------------------------------------

with Ada.Unchecked_Deallocation;

with Util.Beans.Objects;
with Util.Log.Loggers;
with Ada.Exceptions;
with ASF.Utils;
with ASF.Parts;
with ASF.Parts.Upload_Method;
with ASF.Converters;
with ASF.Requests;
with ASF.Components.Base;
with ASF.Components.Utils;
with ASF.Components.Root;
with ASF.Events.Faces.Actions;
with ASF.Applications.Main;
with ASF.Applications.Views;
package body ASF.Components.Html.Forms is

   --  The logger
   Log : constant Util.Log.Loggers.Logger := Util.Log.Loggers.Create ("ASF.Components.Html.Forms");

   FORM_ATTRIBUTE_NAMES      : Util.Strings.String_Set.Set;

   INPUT_ATTRIBUTE_NAMES     : Util.Strings.String_Set.Set;

   TEXTAREA_ATTRIBUTE_NAMES  : Util.Strings.String_Set.Set;

   FILE_ATTRIBUTE_NAMES      : Util.Strings.String_Set.Set;

   procedure Free is
      new Ada.Unchecked_Deallocation (Object => ASF.Validators.Validator'Class,
                                      Name   => ASF.Validators.Validator_Access);

   --  ------------------------------
   --  Input Component
   --  ------------------------------

   --  ------------------------------
   --  Find the form component which contains the input component.
   --  Returns null if the input is not within a form component.
   --  ------------------------------
   function Get_Form (UI : in UIInput) return UIForm_Access is
      use type ASF.Components.Base.UIComponent_Access;

      Parent : ASF.Components.Base.UIComponent_Access := UI.Get_Parent;
   begin
      while Parent /= null loop
         if Parent.all in UIForm'Class then
            return UIForm'Class (Parent.all)'Access;
         end if;
         Parent := Parent.Get_Parent;
      end loop;
      return null;
   end Get_Form;

   --  ------------------------------
   --  Check if this component has the required attribute set.
   --  ------------------------------
   function Is_Required (UI      : in UIInput;
                         Context : in Faces_Context'Class) return Boolean is
      Attr : constant EL.Objects.Object := UI.Get_Attribute (Name    => REQUIRED_NAME,
                                                             Context => Context);
   begin
      if EL.Objects.Is_Null (Attr) then
         return True;
      end if;
      return EL.Objects.To_Boolean (Attr);
   end Is_Required;

   --  ------------------------------
   --  Get the value of the component.  If the component has a submitted value, returns it.
   --  If the component has a local value which is not null, returns it.
   --  Otherwise, if we have a Value_Expression evaluate and returns the value.
   --  ------------------------------
   overriding
   function Get_Value (UI : in UIInput) return EL.Objects.Object is
   begin
      if not Util.Beans.Objects.Is_Null (UI.Submitted_Value) then
         return UI.Submitted_Value;
      else
         return Text.UIOutput (UI).Get_Value;
      end if;
   end Get_Value;

   --  ------------------------------
   --  Set the input component as a password field.
   --  ------------------------------
   procedure Set_Secret (UI    : in out UIInput;
                         Value : in Boolean) is
   begin
      UI.Is_Secret := Value;
   end Set_Secret;

   --  ------------------------------
   --  Render the input element.
   --  ------------------------------
   procedure Render_Input (UI       : in UIInput;
                           Context  : in out Faces_Context'Class;
                           Write_Id : in Boolean := True) is
      Writer : constant Response_Writer_Access := Context.Get_Response_Writer;
      Value  : constant EL.Objects.Object := UIInput'Class (UI).Get_Value;
   begin
      Writer.Start_Element ("input");
      if UI.Is_Secret then
         Writer.Write_Attribute (Name => "type", Value => "password");
      else
         Writer.Write_Attribute (Name => "type", Value => "text");
      end if;
      Writer.Write_Attribute (Name => "name", Value => UI.Get_Client_Id);
      if not EL.Objects.Is_Null (Value) then
         declare
            Convert : constant access Converters.Converter'Class
              := UIInput'Class (UI).Get_Converter;
         begin
            if Convert /= null and Util.Beans.Objects.Is_Null (UI.Submitted_Value) then
               Writer.Write_Attribute (Name  => "value",
                                       Value => Convert.To_String (Value => Value,
                                                                   Component => UI,
                                                                   Context => Context));
            else
               Writer.Write_Attribute (Name => "value", Value => Value);
            end if;
         end;
      end if;
      UI.Render_Attributes (Context, INPUT_ATTRIBUTE_NAMES, Writer, Write_Id);
      Writer.End_Element ("input");
   end Render_Input;

   overriding
   procedure Encode_Begin (UI      : in UIInput;
                           Context : in out Faces_Context'Class) is
   begin
      if UI.Is_Rendered (Context) then
         UIInput'Class (UI).Render_Input (Context);
      end if;
   end Encode_Begin;

   --  ------------------------------
   --  Get the input parameter from the submitted context.  This operation is called by
   --  <tt>Process_Decodes</tt> to retrieve the request parameter associated with the component.
   --  ------------------------------
   function Get_Parameter (UI      : in UIInput;
                           Context : in Faces_Context'Class) return String is
      Id  : constant Unbounded_String := UI.Get_Client_Id;
   begin
      return Context.Get_Parameter (To_String (Id));
   end Get_Parameter;

   overriding
   procedure Process_Decodes (UI      : in out UIInput;
                              Context : in out Faces_Context'Class) is
   begin
      if not UI.Is_Rendered (Context) then
         return;
      end if;
      declare
         Id  : constant Unbounded_String := UI.Get_Client_Id;
         Val : constant String := UIInput'Class (UI).Get_Parameter (Context);
      begin
         --  SCz 2017-05-25: we don't know with AWS whether a parameter is
         --  submitted or not.  It returns the empty string when a parameter
         --  is not found.  If the value is empty and not required,
         --  don't set the valid flag so that we get the JSF behavior.
         if Val'Length = 0 and then not UI.Is_Required (Context) then
            return;
         end if;
         if not UI.Is_Secret then
            Log.Debug ("Set input parameter {0} -> {1}", Id, Val);
         end if;
         UI.Submitted_Value := UIInput'Class (UI).Convert_Value (Val, Context);
         UI.Is_Valid := True;

      exception
         when E : others =>
            UI.Is_Valid := False;
            Log.Info (Utils.Get_Line_Info (UI)
                      & ": Exception raised when converting value {0} for component {1}: {2}",
                      Val, To_String (Id), Ada.Exceptions.Exception_Name (E));
      end;
   end Process_Decodes;

   --  ------------------------------
   --  Perform the component tree processing required by the <b>Process Validations</b>
   --  phase of the request processing lifecycle for all facets of this component,
   --  all children of this component, and this component itself, as follows:
   --  <ul>
{'fraction_non_alphanumeric': 0.1541294712528872, 'fraction_numerical': 0.005431050627379986, 'mean_word_length': 1.6392092257001647, 'pattern_counts': {'":': 2, '<': 19, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3202627', 'n_tokens_mistral': 4249, 'n_tokens_neox': 4120, 'n_words': 1347}