--          This file is part of SmallEiffel The GNU Eiffel Compiler.
--          Copyright (C) 1994-98 LORIA - UHP - CRIN - INRIA - FRANCE
--            Dominique COLNET and Suzanne COLLIN - colnet@loria.fr
--                       http://SmallEiffel.loria.fr
-- SmallEiffel is  free  software;  you can  redistribute it and/or modify it
-- under the terms of the GNU General Public License as published by the Free
-- Software  Foundation;  either  version  2, or (at your option)  any  later
-- version. SmallEiffel is distributed in the hope that it will be useful,but
-- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-- or  FITNESS FOR A PARTICULAR PURPOSE.   See the GNU General Public License
-- for  more  details.  You  should  have  received a copy of the GNU General
-- Public  License  along  with  SmallEiffel;  see the file COPYING.  If not,
-- write to the  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-- Boston, MA 02111-1307, USA.
--
deferred class E_FEATURE
   --
   -- For all possible Features : procedure, function, attribute,
   -- constants, once procedure, once function, ...
   --

inherit GLOBALS;

feature

   base_class: BASE_CLASS;
         -- The class where the feature is really written.

   names: FEATURE_NAME_LIST;
         -- All the names of the feature.

   arguments: FORMAL_ARG_LIST is
         -- Arguments if any.
      deferred
      end;

   result_type: TYPE is
         -- Result type if any.
      deferred
      end;

   header_comment: COMMENT;
         -- Header comment for a routine or following comment for
         -- an attribute.

   obsolete_mark: MANIFEST_STRING is
         -- The `obsolete' mark if any.
      deferred
      end;

   require_assertion: E_REQUIRE is
         -- Not Void if any.
      deferred
      end;

   ensure_assertion: E_ENSURE is
         -- Not Void if any.
      deferred
      end;

   clients: CLIENT_LIST;
         -- Authorized clients list of the corresponding feature
         -- clause in the base definition class.

   is_deferred: BOOLEAN is
         -- Is it a deferred feature ?
      deferred
      end;

   frozen mapping_c_name_in(str: STRING) is
      do
         base_class.mapping_c_in(str);
         str.append(first_name.to_key);
      end;

   frozen mapping_c_name is
      local
         s: STRING;
      do
         s := "    ";
         s.clear;
         mapping_c_name_in(s);
         cpp.put_string(s);
      end;

   frozen base_class_name: CLASS_NAME is
         -- Name of the class where the feature is really written.
      do
         Result := base_class.name;
      end;

   frozen first_name: FEATURE_NAME is
         -- Return the principal (first) name of the feature.
      do
         Result := names.item(1);
      ensure
         Result /= void
      end;

   start_position: POSITION is
      do
         Result := first_name.start_position;
      end;

   to_run_feature(t: TYPE; fn: FEATURE_NAME): RUN_FEATURE is
         -- If possible, gives the checked runnable feature for `t'.
         -- Note: corresponding run_class dictionary is updated
         --       with this new feature.
      require
         t.is_run_type;
         fn /= Void;
      deferred
      ensure
         Result /= Void implies t.run_class.at(fn) = Result;
         Result = Void implies nb_errors > 0
      end;

   can_hide(other: E_FEATURE; rc: RUN_CLASS): BOOLEAN is
         -- True when headings of Current can be hide with
         -- heading of `other' in `rc'.
      require
         Current /= other;
      do
         if result_type /= other.result_type then
            if result_type = Void or else other.result_type = Void then
               eh.add_position(other.start_position);
               error(start_position,"One has Result but not the other.");
            end;
         end;
         if arguments /= other.arguments then
            if arguments = Void or else other.arguments = Void then
               eh.add_position(other.start_position);
               error(start_position,"One has argument(s) but not the other.");
            elseif arguments.count /= other.arguments.count then
               eh.add_position(other.start_position);
               error(start_position,"Incompatible number of arguments.");
            end;
         end;
         if nb_errors = 0 then
            if result_type /= Void then
               if not result_type.is_a_in(other.result_type,rc) then
                  eh.append(em1);
                  eh.append(rc.current_type.run_time_mark);
                  eh.append(fz_dot);
                  eh.print_as_error;
               end;
            end;
         end;
         if nb_errors = 0 then
            if arguments /= Void then
               if not arguments.is_a_in(other.arguments,rc) then
                  eh.add_position(other.start_position);
                  eh.add_position(start_position)
                  eh.append(em1);
                  eh.append(rc.current_type.run_time_mark);
                  eh.append(fz_dot);
                  eh.print_as_error;
               end;
            end;
         end;
         Result := nb_errors = 0;
         if Result then
            merge_header_comments(other);
         end;
      end;

   frozen check_obsolete(caller: POSITION) is
      require
         caller /= Void
      do
         if obsolete_mark /= Void then
            if not small_eiffel.short_flag then
               eh.add_position(caller);
               eh.append("This feature is obsolete :%N");
               eh.append(obsolete_mark.to_string);
               eh.add_position(start_position);
               eh.print_as_warning;
            end
         end;
      end;

   set_header_comment(hc: like header_comment) is
      do
         header_comment := hc;
      end;

   pretty_print is
      require
         fmt.indent_level = 1;
      deferred
      ensure
         fmt.indent_level = 1;
      end;

   frozen pretty_print_profile is
      do
         pretty_print_names;
         fmt.set_indent_level(2);
         pretty_print_arguments;
         fmt.set_indent_level(3);
         if result_type /= Void then
            fmt.put_string(": ");
            result_type.pretty_print;
         end;
      end;

feature {PARENT_LIST}

   frozen is_merge_with(other: E_FEATURE; rc: RUN_CLASS): BOOLEAN is
         -- True when headings of Current can be merge with heading
         -- of `other' in `rc'.
      require
         Current /= other;
         eh.empty
      do
         Result := is_merge_with1(other);
         if Result then
            if is_deferred then
               if other.is_deferred then
               else
                  Result := is_merge_with2(other,rc);
               end;
            else
               Result := is_merge_with2(other,rc);
            end;
            if Result then
               merge_header_comments(other);
            end;
         end;
      ensure
         Result = eh.empty
      end;

feature {PARENT}

   frozen try_to_undefine(fn: FEATURE_NAME; bc: BASE_CLASS):
      DEFERRED_ROUTINE is
         -- Compute the corresponding deferred feature for `Current'.
         -- This occurs for example when `bc' has an undefine clause 
         -- for `fn' which refer to `Current'.
         -- The Result is never Void because `fatal_error' may be called. 
         -- Also check VDUS.
      require
         fn /= Void;
         bc.name.is_subclass_of(base_class_name)
      local
         fn2: FEATURE_NAME;
      do
         -- For (VDUS) :
         eh.add_position(fn.start_position);
         fn2 :=  names.feature_name(fn.to_key);
         if fn2 /= Void then
            fn2.undefine_in(bc);
         end;
         eh.cancel;
         --
         Result := try_to_undefine_aux(fn,bc);
         if Result /= Void then
            Result.set_clients(clients);
            merge_header_comments(Result);
         else
            bc.fatal_undefine(fn);
         end;
      ensure
         Result /= Void
      end;

feature {FEATURE_CLAUSE,E_FEATURE}

   set_clients(c: like clients) is
      require
         c /= Void;
      do
         clients := c;
      ensure
         clients = c;
      end;

feature {FEATURE_CLAUSE}

   add_into(fd: DICTIONARY[E_FEATURE,STRING]) is
         -- Also check for multiple definitions.
      local
         i: INTEGER;
         fn: FEATURE_NAME;
      do
         base_class := names.item(1).start_position.base_class;
         from
            i := 1;
         until
            i > names.count
         loop
            fn := names.item(i);
            if fd.has(fn.to_key) then
               fn := fd.at(fn.to_key).first_name;
               eh.add_position(fn.start_position);
               eh.add_position(names.item(i).start_position);
               eh.append("Double definition of feature ");
               eh.append(fn.to_string);
               eh.append(fz_dot);
               eh.print_as_error;
            else
               fd.put(Current,fn.to_key);
            end;
            i := i + 1;
         end;
      end;

feature {C_PRETTY_PRINTER}

   stupid_switch(up_rf: RUN_FEATURE; r: ARRAY[RUN_CLASS]): BOOLEAN is
         -- True when it is stupid to perform such a switch.
         -- Assume all `base_feature' of `r' is Current.
      require
         run_control.boost;
         small_eiffel.is_ready;
         up_rf.base_feature = Current;
         up_rf.run_class.running = r;
         r.count > 1
      deferred
      end;

feature {E_FEATURE}

   frozen is_merge_with2aux(other: E_FEATURE; rc: RUN_CLASS): BOOLEAN is
      do
         Result := true;
         if result_type /= Void then
            Result := result_type.is_a_in(other.result_type,rc);
         end;
         if Result and then arguments /= Void then
            Result := arguments.is_a_in(other.arguments,rc);
         end;
      end;

feature {NONE}

   frozen pretty_print_names is
         -- Print only the names of the feature.
      local
         i: INTEGER;
      do
         from
            i := 1;
            names.item(i).declaration_pretty_print;
            i := i + 1;
         until
            i > names.count
         loop
            fmt.put_string(", ");
            names.item(i).declaration_pretty_print;
            i := i + 1;
         end;
      end;

   pretty_print_arguments is
      deferred
      end;

   make_e_feature(n: like names) is
      require
         n.count >= 1;
      do
         names := n;
      ensure
         names = n
      end;

   try_to_undefine_aux(fn: FEATURE_NAME;
                       bc: BASE_CLASS): DEFERRED_ROUTINE is
      require
         fn /= Void;
         bc /= Void
      deferred
      end;

   em1: STRING is " Cannot inherit these features in "

   frozen merge_header_comments(other: E_FEATURE) is
         -- Falling down of the `header_comment' for command short.
      do
         if small_eiffel.short_flag then
            if header_comment = Void then
               header_comment := other.header_comment;
            elseif other.header_comment = Void then
               other.set_header_comment(header_comment);
            end;
         end;
      end;

   frozen is_merge_with1(other: E_FEATURE): BOOLEAN is
         -- Step 1 : check for the number of arguments and
         -- for the existance of a result type.
      require
         Current /= other;
         eh.empty
      do
         if result_type = Void then
            Result := other.result_type = Void;
         else
            Result := other.result_type /= Void;
         end;
         if Result then
            if arguments = Void then
               Result := other.arguments = Void;
            else
               Result := other.arguments /= Void;
            end;
         end;
      ensure
         Result = eh.empty
      end;

   frozen is_merge_with2(other: E_FEATURE; rc: RUN_CLASS): BOOLEAN is
         -- Step 2 : check for arguments/result compatibility.
      do
         Result := is_merge_with2aux(other,rc);
         if not Result then
            eh.cancel;
            Result := other.is_merge_with2aux(Current,rc);
         end;
      end;

invariant

   names /= Void;

end -- E_FEATURE

