
with Ada.Text_IO;

with Asis.Declarations;
with Asis.Definitions;
with Asis.Elements;
with Asis.Exceptions;
with Asis.Expressions;
with Asis.Report;

with Asis.Toolkit.ObjectAda.Elements;
with Asis.Toolkit.ObjectAda.Expressions;

with Ada.Characters.Handling;

package body Asis.Toolkit.ObjectAda.Declarations is

  package TOOLELEM renames Asis.Toolkit.ObjectAda.Elements;
  package TOOLEXPR renames Asis.Toolkit.ObjectAda.Expressions;

  Debug : constant Boolean := 
	      Asis.Report.Debug ( "Asis.Toolkit.ObjectAda.Declarations.Debug" );

  function Name_Image (Declaration : in Asis.Declaration) return String is
  begin
    case Asis.Elements.Declaration_Kind(Declaration) is
      when Asis.Not_A_Declaration =>
	raise Asis.Exceptions.Asis_Inappropriate_Element;

      when others =>
        return Ada.Characters.Handling.To_String
		  (Asis.Declarations.Defining_Name_Image
                      (Asis.Declarations.Names(Declaration)(1)));
    end case;
  end Name_Image;

  function Parent_Full_Expanded_Name_Image 
			   (Declaration : in Asis.Declaration) return String is
  begin
    if Debug then
      Ada.Text_IO.Put_Line(Item => "PFENI:" & Asis.Declaration_Kinds'Image
			   (Asis.Elements.Declaration_Kind(Declaration)));
    end if;
    case Asis.Elements.Declaration_Kind(Declaration) is
      when Asis.Not_A_Declaration =>
	return "";

      when others =>
	return Full_Expanded_Name_Image
	         (TOOLELEM.Enclosing_Declaration(Declaration));
    end case;
  end Parent_Full_Expanded_Name_Image;

  function Full_Expanded_Name_Image 
		   (Declaration : in Asis.Declaration) return String is
    Parent : Asis.Declaration;
  begin
    if Debug then
      Ada.Text_IO.Put_Line(Item => "FENI:" & Asis.Declaration_Kinds'Image
			   (Asis.Elements.Declaration_Kind(Declaration)));
    end if;
    case Asis.Elements.Declaration_Kind(Declaration) is
      when Asis.Not_A_Declaration =>
	return "";

      when others =>
	Parent := TOOLELEM.Enclosing_Declaration(Declaration);
	case Asis.Elements.Element_Kind(Parent) is
	  when Asis.A_Declaration =>
            return Full_Expanded_Name_Image(Parent)
	       & "." & Ada.Characters.Handling.To_String
		      (Asis.Declarations.Defining_Name_Image
                          (Asis.Declarations.Names(Declaration)(1)));
	  when Asis.Not_An_Element =>
	    return Ada.Characters.Handling.To_String
		      (Asis.Declarations.Defining_Name_Image
                          (Asis.Declarations.Names(Declaration)(1)));
	  when others =>
	    null;  -- not happen
        end case;
    end case;
    return "";
  end Full_Expanded_Name_Image;


  function Type_Constraint (Declaration : in Asis.Declaration)
		      return Asis.Constraint is

    Type_Kind : Asis.Type_Kinds;
    Def_Kind  : Asis.Definition_Kinds;
    View      : Asis.Definition;
  begin

    case Asis.Elements.Declaration_Kind(Declaration) is
      when Asis.A_Task_Type_Declaration =>
        return Asis.Nil_Element;

      when Asis.A_Protected_Type_Declaration =>
        return Asis.Nil_Element;

      when Asis.An_Ordinary_Type_Declaration |
           Asis.A_Private_Type_Declaration |
           Asis.A_Private_Extension_Declaration |
           Asis.A_Subtype_Declaration =>

    -- Check view of first subtype.
    -- Let Type_Declaration_View determine what is appropriate;
    -- exception returns Nil_Element;

    View := Asis.Declarations.Type_Declaration_View(Declaration);

    Def_Kind := Asis.Elements.Definition_Kind(View);
    case Def_Kind is
      when Asis.A_Subtype_Indication =>

	    declare
	      Constraint : Asis.Constraint :=
	         Asis.Definitions.Subtype_Constraint(View);
	    begin
	      if not Asis.Elements.Is_Nil(Constraint) then
		return Constraint;

	      else
		-- recurse to look for a constraint
	        return Type_Constraint
		    (Asis.Declarations.Corresponding_Last_Constraint
							     (Declaration));

	      end if;
	    end;

      when Asis.A_Type_Definition =>

        Type_Kind := Asis.Elements.Type_Kind(View);
        case Type_Kind is
	  when Asis.Not_A_Type_Definition =>
            null;

          when A_Root_Type_Definition =>
            null;

          when Asis.An_Enumeration_Type_Definition =>
            null;

          when Asis.A_Signed_Integer_Type_Definition =>
            return Asis.Definitions.Integer_Constraint(View);

          when Asis.A_Modular_Type_Definition =>
            null;

          when Asis.A_Floating_Point_Definition =>
            return Asis.Definitions.Real_Range_Constraint(View);

          when Asis.An_Ordinary_Fixed_Point_Definition =>
            return Asis.Definitions.Real_Range_Constraint(View);

          when Asis.A_Decimal_Fixed_Point_Definition =>
            return Asis.Definitions.Real_Range_Constraint(View);

          when Asis.An_Unconstrained_Array_Definition =>
            null;
	    
          when Asis.A_Constrained_Array_Definition =>
            null;

          when Asis.An_Access_Type_Definition =>
	    null;

          when Asis.A_Record_Type_Definition =>
            null;

          when Asis.A_Derived_Record_Extension_Definition |
	       Asis.A_Tagged_Record_Type_Definition =>
            null;

          when Asis.A_Derived_Type_Definition =>
	    -- recurse to check the parent subtype
	    -- this will unwind subtypes and derivations
	    declare
	      Constraint : Asis.Constraint :=
	         Asis.Definitions.Subtype_Constraint
	            (Asis.Definitions.Parent_Subtype_Indication(View));
	    begin
	      if not Asis.Elements.Is_Nil(Constraint) then
		return Constraint;

	      else
		-- recurse to look for a constraint
	        return Type_Constraint
			 (Asis.Definitions.Corresponding_Parent_Subtype(View));

	      end if;
	    end;

        end case;

      when Asis.A_Private_Type_Definition =>
	-- recurse to check the parent subtype
	-- this will unwind each derivation
	return Type_Constraint
		     (Asis.Declarations.Corresponding_Type_Declaration
			  (Asis.Elements.Enclosing_Element(View)));
			     --####(Declaration));

      when others =>
        null;
    end case;


      when Asis.A_Formal_Type_Declaration =>
	--## may implement later
        null;

      when others =>
        null;
    end case;

    return Asis.Nil_Element;

  exception
    when others =>
      return Asis.Nil_Element;
  end Type_Constraint;



  function Corresponding_Class_Type (Declaration : in Asis.Declaration)
		      return Asis.Declaration is

    Type_Kind : Asis.Type_Kinds;
    Def_Kind  : Asis.Definition_Kinds;
    View      : Asis.Definition;
  begin

    case Asis.Elements.Declaration_Kind(Declaration) is
      when Asis.A_Task_Type_Declaration =>
        return Declaration;

      when Asis.A_Protected_Type_Declaration =>
        return Declaration;

      when Asis.An_Ordinary_Type_Declaration |
           Asis.A_Private_Type_Declaration |
           Asis.A_Private_Extension_Declaration |
           Asis.A_Subtype_Declaration =>
	null;  -- pass through
 
      when Asis.A_Formal_Type_Declaration =>
	--## may implement later
        return Declaration;

      when others =>
	raise Asis.Exceptions.Asis_Inappropriate_Element;
    end case;

    -- Check view of first subtype.
    -- Let first subtype verify Declaration is appropriate;
    -- exception returns Not_A_Type_Class;

    -- resolve subtypes to a first_subtype
    View := Asis.Declarations.Type_Declaration_View
			  (Asis.Declarations.Corresponding_First_Subtype
			     (Declaration));

    Def_Kind := Asis.Elements.Definition_Kind(View);
    case Def_Kind is
      when Asis.A_Type_Definition =>

        Type_Kind := Asis.Elements.Type_Kind(View);
        case Type_Kind is

          when Asis.A_Derived_Type_Definition =>
	    -- recurse to check the parent subtype
	    -- this will unwind subtypes and derivations
	    return Corresponding_Class_Type
			 (Asis.Definitions.Corresponding_Parent_Subtype(View));

	  when others =>
	    return Asis.Elements.Enclosing_Element(View);

        end case;

      when Asis.A_Private_Type_Definition =>
	-- recurse to check the parent subtype
	-- this will unwind each derivation
	return Corresponding_Class_Type
		     (Asis.Declarations.Corresponding_Type_Declaration
			  (Asis.Elements.Enclosing_Element(View)));
			     --###(Declaration));

      when Asis.A_Tagged_Private_Type_Definition |
	   Asis.A_Private_Extension_Definition =>
	-- The full type of these must be a tagged record
	return Corresponding_Class_Type
		     (Asis.Declarations.Corresponding_Type_Declaration
			  (Asis.Elements.Enclosing_Element(View)));
			     --###(Declaration));

      when others =>
	-- should not happen
        null;
    end case;

    raise Asis.Exceptions.Asis_Inappropriate_Element;

  exception
    when others =>
      raise Asis.Exceptions.Asis_Inappropriate_Element;
  end Corresponding_Class_Type;

  function Type_Class (Declaration : in Asis.Declaration)
			     return Type_Classes is

    Type_Kind     : Asis.Type_Kinds;
    Def_Kind      : Asis.Definition_Kinds;
    First_Subtype : Asis.Declaration;
    View          : Asis.Definition;
  begin

    case Asis.Elements.Declaration_Kind(Declaration) is
      when Asis.A_Task_Type_Declaration =>
        return A_Task_Class;

      when Asis.A_Protected_Type_Declaration =>
        return A_Protected_Class;

      when Asis.An_Ordinary_Type_Declaration |
           Asis.A_Private_Type_Declaration |
           Asis.A_Private_Extension_Declaration |
           Asis.A_Subtype_Declaration =>
	null;  -- pass through
 
      when Asis.A_Formal_Type_Declaration =>
	--## may implement later
        return Not_A_Type_Class;

      when others =>
        return Not_A_Type_Class;
    end case;

    -- Check view of first subtype.
    -- Let first subtype verify Declaration is appropriate;
    -- exception returns Not_A_Type_Class;

    -- resolve subtypes to a first_subtype
    First_Subtype := Asis.Declarations.Corresponding_First_Subtype(Declaration);
    View := Asis.Declarations.Type_Declaration_View(First_Subtype);

    Def_Kind := Asis.Elements.Definition_Kind(View);
    case Def_Kind is
      when Asis.A_Type_Definition =>

        Type_Kind := Asis.Elements.Type_Kind(View);
        case Type_Kind is
	  when Asis.Not_A_Type_Definition =>
            return Not_A_Type_Class;

          when A_Root_Type_Definition =>
            return Not_A_Type_Class;

          when Asis.An_Enumeration_Type_Definition =>
            return An_Enumeration_Class;

          when Asis.A_Signed_Integer_Type_Definition =>
            return A_Signed_Integer_Class;

          when Asis.A_Modular_Type_Definition =>
            return A_Modular_Integer_Class;

          when Asis.A_Floating_Point_Definition =>
            return A_Float_Class;

          when Asis.An_Ordinary_Fixed_Point_Definition =>
            return An_Ordinary_Fixed_Class;

          when Asis.A_Decimal_Fixed_Point_Definition =>
            return A_Decimal_Fixed_Class;

          when Asis.An_Unconstrained_Array_Definition =>
            return An_Unconstrained_Array_Class;
	    
          when Asis.A_Constrained_Array_Definition =>
            return A_Constrained_Array_Class;

          when Asis.An_Access_Type_Definition =>
	    case Asis.Elements.Access_Type_Kind(View) is
              when Asis.Access_To_Object_Definition =>
                return An_Access_To_Object_Class;

              when Asis.Access_To_Subprogram_Definition =>
                return An_Access_To_Subprogram_Class;

	      when Asis.Not_An_Access_Type_Definition =>
		-- would be an error;
                return Not_A_Type_Class;
	    end case;

          when Asis.A_Record_Type_Definition =>
            return An_Untagged_Record_Class;

          when Asis.A_Derived_Record_Extension_Definition |
	       Asis.A_Tagged_Record_Type_Definition =>
            return A_Tagged_Record_Class;

          when Asis.A_Derived_Type_Definition =>
	    -- recurse to check the parent subtype
	    -- this will unwind subtypes and derivations
	    return Type_Class
			 (Asis.Definitions.Corresponding_Parent_Subtype(View));

        end case;

      when Asis.A_Private_Type_Definition =>
	-- recurse to check the parent subtype
	-- this will unwind each derivation
	return Type_Class
		     (Asis.Declarations.Corresponding_Type_Declaration
			     (First_Subtype));

      when Asis.A_Tagged_Private_Type_Definition |
	   Asis.A_Private_Extension_Definition =>
	-- The full type of these must be a tagged record
        return A_Tagged_Record_Class;

      when Asis.A_Task_Definition =>
        return A_Task_Class;

      when Asis.A_Protected_Definition =>
        return A_Protected_Class;

      when others =>
        return Not_A_Type_Class;
    end case;

    return Not_A_Type_Class;

  exception
    when others =>
      return Not_A_Type_Class;
  end Type_Class;

  function Is_Record_Subtype (Declaration : in Asis.Declaration)
			     return Boolean is

    Type_Kind : Asis.Type_Kinds;
    Def_Kind  : Asis.Definition_Kinds;
    View      : Asis.Definition;
    First_Subtype : Asis.Declaration;
  begin
    -- Check view of first subtype.
    -- Let first subtype verify Declaration is appropriate;
    -- exception returns false;

            if Debug then
              --%
              Ada.Text_IO.Put( "Is_Record_Subtype:DECL:argument");
              Asis.Toolkit.ObjectAda.Elements.Print_Element
		        (Declaration, verbose => true, span => True);
	    end if;

    -- resolve subtypes to a first_subtype
    First_Subtype := Asis.Declarations.Corresponding_First_Subtype(Declaration);

            if Debug then
              --%
              Ada.Text_IO.Put( "Is_Record_Subtype:DECL:first_subtype");
              Asis.Toolkit.ObjectAda.Elements.Print_Element
		        (First_Subtype, verbose => true, span => True);
	    end if;

    View := Asis.Declarations.Type_Declaration_View(First_Subtype);

    Def_Kind := Asis.Elements.Definition_Kind(View);
    case Def_Kind is
      when Asis.A_Type_Definition =>

        Type_Kind := Asis.Elements.Type_Kind(View);
        case Type_Kind is
          when Asis.A_Record_Type_Definition | 
               Asis.A_Derived_Record_Extension_Definition |
	       Asis.A_Tagged_Record_Type_Definition =>
	    return True;

          when Asis.A_Derived_Type_Definition =>
	    -- recurse to check the parent subtype
	    -- this will unwind subtypes and derivations
	    return Is_Record_Subtype
			 (Asis.Definitions.Corresponding_Parent_Subtype(View));

          when others =>
            if Debug then
              --%
              Ada.Text_IO.Put( "Is_Record_Subtype:FALSE:type_kind:others");
              Asis.Toolkit.ObjectAda.Elements.Print_Element
		        (Declaration, verbose => true, span => True);
	    end if;
	    return False;
        end case;

      when Asis.A_Private_Type_Definition =>
	-- recurse to check the parent subtype
	-- this will unwind each derivation
	return Is_Record_Subtype
		     (Asis.Declarations.Corresponding_Type_Declaration
			     (First_Subtype));

      when Asis.A_Tagged_Private_Type_Definition |
	   Asis.A_Private_Extension_Definition =>
	-- The full type of these must be a record
	return True;

      when others =>
            if Debug then
              --%
              Ada.Text_IO.Put( "Is_Record_Subtype:FALSE:def_kind:others");
              Asis.Toolkit.ObjectAda.Elements.Print_Element
		        (Declaration, verbose => true, span => True);
	    end if;
	return False;
    end case;

    return False;

  exception
    when others =>
            if Debug then
              --%
              Ada.Text_IO.Put( "Is_Record_Subtype:FALSE:exception");
              Asis.Toolkit.ObjectAda.Elements.Print_Element
		        (Declaration, verbose => true, span => True);
	    end if;
      return False;
  end Is_Record_Subtype;

  function Components(Definition : in Asis.Definition)
      return Asis.Element_List is

    Result_List  : Asis.Element_List(1..1000); --##
    Result_Count : Asis.Asis_Natural := 0;
    Discrim_Part : Asis.Element;
    Rec_Def      : Asis.Definition;

    procedure Flatten(Record_Definition : in Asis.Definition) is
      Comp_List : Asis.Element_List := 
		         Asis.Definitions.Record_Components(Record_Definition);
      use type Asis.Element_List;
    begin
      -- assign the Result_List components up to a variant part, then
      -- flatten the variant part into variants and their components
      for I in Comp_List'Range loop
	-- first look for component decls
	case Asis.Elements.Declaration_Kind(Comp_List(I)) is
	  when Asis.A_Component_Declaration =>
	    Result_Count := Result_Count + 1;
	    Result_List(Result_Count) := Comp_List(I);
	  when others =>
	    null;
	end case;

	case Asis.Elements.Definition_Kind(Comp_List(I)) is
	  when Asis.A_Variant_Part =>
	    declare
	      Variant_List : Asis.Element_List := 
			        Asis.Definitions.Variants(Comp_List(I));
	    begin
	      for V in Variant_List'Range loop
	        Flatten(Variant_List(V));
	      end loop;
	    end;
	  when others =>
	    null;
	end case;
      end loop;
    end Flatten;

  begin

    --%
    --%Ada.Text_IO.Put( "Components:");
    --%Asis.Toolkit.ObjectAda.Elements.Print_Element
	      --%(Definition, verbose => true, span => True);
    --%Ada.Text_IO.Put( "Components:Enclosing");
    --%Asis.Toolkit.ObjectAda.Elements.Print_Element
	      --%(Asis.Elements.Enclosing_Element(Definition),
		  --%verbose => true, span => True);

      -- check discrims of the enclosing declaration of the definition
      Discrim_Part := Asis.Declarations.Discriminant_Part
			(Asis.Elements.Enclosing_Element(Definition));
      if not Asis.Elements.Is_Nil(Discrim_Part) then
	declare
          Discrim_List : Asis.Element_List := 
			     Asis.Definitions.Discriminants(Discrim_Part);
	begin
	  for I in Discrim_List'range loop
	    Result_Count := Result_Count + 1;
	    Result_List(Result_Count) := Discrim_List(I);
	  end loop;
	end;
      end if;

      Rec_Def := Asis.Definitions.Record_Definition(Definition);
      case Asis.Elements.Definition_Kind(Rec_Def) is
	when Asis.A_Record_Definition =>
          Flatten(Rec_Def);
          return Result_List(1 .. Result_Count);

	when Asis.A_Null_Record_Definition =>
          return Result_List(1 .. Result_Count);

        when others =>
	  raise Asis.Exceptions.Asis_Inappropriate_Element;
      end case;
  end Components;

  function Record_Components (Declaration : in Asis.Declaration)
		             return Asis.Element_List is
    Type_Kind : Asis.Type_Kinds;
    Def_Kind  : Asis.Definition_Kinds;
    View      : Asis.Definition;

   begin

    -- resolve subtypes to a first_subtype
    View := Asis.Declarations.Type_Declaration_View
			  (Asis.Declarations.Corresponding_First_Subtype
			     (Declaration));
  
    Def_Kind := Asis.Elements.Definition_Kind(View);
    case Def_Kind is
      when Asis.A_Type_Definition =>

        Type_Kind := Asis.Elements.Type_Kind(View);
        case Type_Kind is
          when Asis.A_Record_Type_Definition | 
	       Asis.A_Tagged_Record_Type_Definition =>
	    return Components(View);

          when Asis.A_Derived_Record_Extension_Definition =>
            return Asis."&" 
		 (Record_Components
			(Asis.Definitions.Corresponding_Parent_Subtype(View)),
		  Components(View));

          when Asis.A_Derived_Type_Definition =>
	    -- recurse to check the parent subtype
	    -- this will unwind subtypes and derivations
	    return Record_Components
			 (Asis.Definitions.Corresponding_Parent_Subtype(View));

          when others =>
            if Debug then
              --%
              Ada.Text_IO.Put( "Record_Components:type_kind:others");
              Asis.Toolkit.ObjectAda.Elements.Print_Element
		        (Declaration, verbose => true, span => True);
            end if;
	    return Asis.Nil_Element_List;
        end case;

      when Asis.A_Private_Type_Definition |
           Asis.A_Tagged_Private_Type_Definition |
	   Asis.A_Private_Extension_Definition =>

	-- The full type of these must be a record
	-- recurse to check the parent subtype
	-- this will unwind each derivation
            if Debug then
              --%
              Ada.Text_IO.Put( "Record_Components:Priv_Type:Corresp");
              Asis.Toolkit.ObjectAda.Elements.Print_Element
		(Declaration, verbose => true, span => True);
            end if;
	return Record_Components
		     (Asis.Declarations.Corresponding_Type_Declaration
			  (Asis.Elements.Enclosing_Element(View)));

      when others =>
        if Debug then
          --%
          Ada.Text_IO.Put( "Record_Components:def_kind:others");
          Asis.Toolkit.ObjectAda.Elements.Print_Element
		      (Declaration, verbose => true, span => True);
        end if;
	return Asis.Nil_Element_List;
    end case;

  end Record_Components;

  function All_Record_Components (Declaration : in Asis.Declaration)
		             return Asis.Element_List is
  begin
    case Asis.Elements.Declaration_Kind(Declaration) is
      when Asis.An_Ordinary_Type_Declaration |
           Asis.A_Task_Type_Declaration |
           Asis.A_Protected_Type_Declaration |
           Asis.A_Private_Type_Declaration |
           Asis.A_Private_Extension_Declaration |
           Asis.A_Subtype_Declaration |
           Asis.A_Formal_Type_Declaration =>
 
        if Is_Record_Subtype(Declaration) then
          return Record_Components(Declaration);
        else
          if Debug then
            --%
            Ada.Text_IO.Put( "Is_Record_Subtype:FALSE");
            Asis.Toolkit.ObjectAda.Elements.Print_Element
		(Declaration, verbose => true, span => True);
          end if;
          return Asis.Nil_Element_List;
        end if;

      when others =>
        raise Asis.Exceptions.Asis_Inappropriate_Element;
    end case;

  end All_Record_Components;

  function Discriminants (Declaration : in Asis.Declaration)
		             return Asis.Element_List is
    Discrim_Part : Asis.Definition;
  begin
    case Asis.Elements.Declaration_Kind(Declaration) is
      when Asis.An_Ordinary_Type_Declaration |
           Asis.A_Task_Type_Declaration |
           Asis.A_Protected_Type_Declaration |
           Asis.A_Private_Type_Declaration |
           Asis.A_Private_Extension_Declaration |
           Asis.A_Formal_Type_Declaration =>
 
        -- check discrims of the enclosing declaration of the definition
        Discrim_Part := Asis.Declarations.Discriminant_Part(Declaration);
        if not Asis.Elements.Is_Nil(Discrim_Part) then
          return Asis.Definitions.Discriminants(Discrim_Part);
	else
          return Asis.Nil_Element_List;
        end if;

      when others =>
        raise Asis.Exceptions.Asis_Inappropriate_Element;
    end case;

  end Discriminants;

  function Is_Package (Declaration : in Asis.Declaration) return Boolean is
  begin
    case Asis.Elements.Declaration_Kind(Declaration) is
      when Asis.A_Package_Declaration | 
           Asis.A_Package_Body_Declaration | 
           Asis.A_Package_Renaming_Declaration | 
           Asis.A_Generic_Package_Renaming_Declaration | 
           Asis.A_Package_Body_Stub | 
           Asis.A_Generic_Package_Declaration | 
           Asis.A_Package_Instantiation | 
           Asis.A_Formal_Package_Declaration | 
           Asis.A_Formal_Package_Declaration_With_Box =>
	return True;

      when others =>
	return False;

    end case;
  end Is_Package;

  function Is_Generic (Declaration : in Asis.Declaration) return Boolean is
  begin

    --%Ada.Text_IO.Put( "Is_Generic:");
    --%Asis.Toolkit.ObjectAda.Elements.Print_Element
		--%(Declaration, verbose => true, span => True);

    if Asis.Declarations.Is_Subunit(Declaration) then
      return Is_Generic
		   (Asis.Declarations.Corresponding_Body_Stub(Declaration));
    end if;

    case Asis.Elements.Declaration_Kind(Declaration) is
      when Asis.A_Generic_Procedure_Declaration |
           Asis.A_Generic_Function_Declaration |
           Asis.A_Generic_Package_Declaration |
           Asis.A_Generic_Procedure_Renaming_Declaration |
           Asis.A_Generic_Function_Renaming_Declaration |
           Asis.A_Generic_Package_Renaming_Declaration =>
	return True;

      when Asis.A_Procedure_Body_Declaration | 
           Asis.A_Function_Body_Declaration | 
           Asis.A_Package_Body_Declaration | 
           Asis.A_Procedure_Body_Stub | 
           Asis.A_Function_Body_Stub | 
           Asis.A_Package_Body_Stub =>
	return Is_Generic
		   (Asis.Declarations.Corresponding_Declaration(Declaration));

      when others =>
        return False;

    end case;
  exception
    when Asis.Exceptions.Asis_Inappropriate_Element =>
      return False;
  end Is_Generic;

  function Is_Part_Of_Generic (Element : in Asis.Element) 
			      return Boolean is
    Encloser : Asis.Element := Element;
  begin
    -- loop over enclosing elements to see if one is_generic.
    loop
      if Is_Generic(Encloser) then
	return True;
      end if;
      Encloser := Asis.Elements.Enclosing_Element(Encloser); 
      --%Ada.Text_IO.Put( "Is_Part_Of_Generic:Encloser:");
      --%Asis.Toolkit.ObjectAda.Elements.Print_Element
		  --%(Encloser, verbose => true, span => True);
      exit when Asis.Elements.Is_Nil(Encloser); 
    end loop;

    return False;

  exception
    when Asis.Exceptions.Asis_Inappropriate_Element =>
      return False;
  end Is_Part_Of_Generic;

  function Is_Global (Declaration : in Asis.Declaration) return Boolean is
    Encloser : Asis.Element;
  begin
    case Asis.Elements.Declaration_Kind(Declaration) is
      when Not_A_Declaration =>
	return False;

      when others =>
	Encloser := Asis.Elements.Enclosing_Element(Declaration);

	while not Asis.Elements.Is_Nil(Encloser) loop
          if not Is_Package(Encloser) then
	    return False;
	  end if;
	  Encloser := Asis.Elements.Enclosing_Element(Encloser);
	end loop;

	-- all enclosing elements are packages
	return True;

    end case;
  end Is_Global;


  function Object_Subtype (Object : in Asis.Declaration) 
			  return Asis.Declaration is
    View : Asis.Definition;
    Indication : Asis.Expression;
  begin
    case Asis.Elements.Declaration_Kind(Object) is
      when Asis.A_Variable_Declaration | 
           Asis.A_Deferred_Constant_Declaration | 
           Asis.A_Component_Declaration |
           Asis.A_Constant_Declaration =>

        View := Asis.Declarations.Object_Declaration_View(Object);
        case Asis.Elements.Definition_Kind(View) is
	  when Asis.A_Subtype_Indication =>

            return TOOLEXPR.Entity_Declaration
		                  (Asis.Definitions.Subtype_Mark(View));

	  when Asis.A_Component_Definition =>
            return TOOLEXPR.Entity_Declaration
		     (Asis.Definitions.Subtype_Mark
		        (Asis.Definitions.Component_Subtype_Indication(View)));

          when others =>
	  --# could be anon array, A_Type_Definition
	    return Asis.Nil_Element;
	end case;

      when Asis.A_Discriminant_Specification |
           Asis.An_Object_Renaming_Declaration =>
         return TOOLEXPR.Entity_Declaration
                     (Asis.Declarations.Declaration_Subtype_Mark(Object));

      when others =>
	raise Asis.Exceptions.Asis_Inappropriate_Element;
    end case;
    
  end Object_Subtype;

  function Object_Constraint (Object : in Asis.Declaration)
		      return Asis.Constraint is
    View : Asis.Definition;
    Indication : Asis.Expression;
  begin
    case Asis.Elements.Declaration_Kind(Object) is
      when Asis.A_Variable_Declaration | 
           Asis.A_Deferred_Constant_Declaration | 
           Asis.A_Component_Declaration |
           Asis.A_Constant_Declaration =>

        View := Asis.Declarations.Object_Declaration_View(Object);
        case Asis.Elements.Definition_Kind(View) is
	  when Asis.A_Subtype_Indication =>

	    declare
	      Constraint : Asis.Constraint :=
	         Asis.Definitions.Subtype_Constraint(View);
	    begin
	      if not Asis.Elements.Is_Nil(Constraint) then
		return Constraint;

	      else
		-- recurse to look for a constraint
	        return Type_Constraint
		    (Asis.Declarations.Corresponding_Last_Constraint
                       (TOOLEXPR.Entity_Declaration
                          (Asis.Definitions.Subtype_Mark(View))));

	      end if;
	    end;

	  when Asis.A_Component_Definition =>

	    declare
	      Constraint : Asis.Constraint :=
	         Asis.Definitions.Subtype_Constraint
		     (Asis.Definitions.Component_Subtype_Indication(View));
	    begin
	      if not Asis.Elements.Is_Nil(Constraint) then
		return Constraint;

	      else
		-- look for a constraint
	        return Type_Constraint
		    (Asis.Declarations.Corresponding_Last_Constraint
                       (TOOLEXPR.Entity_Declaration
                          (Asis.Definitions.Subtype_Mark
		    (Asis.Definitions.Component_Subtype_Indication(View)))));

	      end if;
	    end;

          when others =>
	  --# could be anon array, A_Type_Definition
	    return Asis.Nil_Element;
	end case;

      when Asis.A_Discriminant_Specification =>
	   return Asis.Nil_Element;
	
      when others =>
	raise Asis.Exceptions.Asis_Inappropriate_Element;
    end case;
  end Object_Constraint;

  function Explicit_Object_Constraint (Object : in Asis.Declaration)
		      return Asis.Constraint is
    View : Asis.Definition;
    Indication : Asis.Expression;
  begin
    case Asis.Elements.Declaration_Kind(Object) is
      when Asis.A_Variable_Declaration | 
           Asis.A_Deferred_Constant_Declaration | 
           Asis.A_Component_Declaration |
           Asis.A_Constant_Declaration =>

        View := Asis.Declarations.Object_Declaration_View(Object);
        case Asis.Elements.Definition_Kind(View) is
	  when Asis.A_Subtype_Indication =>

	    return Asis.Definitions.Subtype_Constraint(View);

	  when Asis.A_Component_Definition =>

	    return Asis.Definitions.Subtype_Constraint
		     (Asis.Definitions.Component_Subtype_Indication(View));

          when others =>
	    --# could be anon array, A_Type_Definition
	    return Asis.Nil_Element;
	end case;

      when Asis.A_Discriminant_Specification =>
	   return Asis.Nil_Element;
	
      when others =>
	raise Asis.Exceptions.Asis_Inappropriate_Element;
    end case;
  end Explicit_Object_Constraint;

end Asis.Toolkit.ObjectAda.Declarations;

