Матрёшка: Преобразование Ada в UML
В предыдущих статьях уже было рассмотрено использование модуля AMF Матрёшки для генерации кода по UML модели. Однако иногда возникает потребность выполнить обратное преобразование — исходный Ada код преобразовать к виду, удобному для визуального восприятия. Часто такое преобразование называют реверс‐инжинирингом. В это статье мы рассмотрим некоторые аспекты разработки подобного инструмента, позволяющего выполнить преобразование исходных текстов Ada программ в модель UML. Полученная модель может быть экспортирована в любую совместимую среду моделирования, где для неё возможно уже создание различных диаграмм.
Тестовый пример
Для демонстрации основных концепций возьмём небольшой тестовый пример:
package Money is
type Money is record
Amount : Float;
end record;
procedure Add (Self : in out Money; Other : Money);
end Money;
Отображение конструкций Ada в UML
Договоримся, что Ada пакеты будут представляться в модели как UML пакеты; Ada типы будут представлены классами UML; поля записи — атрибутами класса; подпрограммы — операциями класса.
Анализ исходного кода
Для анализа исходного кода будет использована реализация стандартного интерфейса ASIS, предназначенная как раз для построения аналогичных инструментов. Интерфейс ASIS представляет текст программы в виде дерева элементов синтаксиса, аннотированного семантической информацией. Для удобства реализации интерфейсов он предоставляет стандартную реализацию шаблона «визитёр», адаптированную для обхода элементов синтаксического дерева. Для его использования требуется определить тип данных, предназначенные для хранения текущего состояния инструмента и две подпрограммы — одна из которых вызывается при воде в очередной узел дерева, а вторая — перед выходом из него. Тип данных для хранения состояния будет содержать поля для хранения элементов UML модели, созданных в процессе обхода дерева. Нам будет вполне достаточно объявить по одному полю каждого для каждого типа используемых UML объектов плюс одно поле для фабрики UML объектов:
type State_Information is record
The_Factory : AMF.Factories.UML_Factories.UML_Factory_Access;
The_Model : AMF.UML.Models.UML_Model_Access;
The_Package : AMF.UML.Packages.UML_Package_Access;
The_Class : AMF.UML.Classes.UML_Class_Access;
The_Property : AMF.UML.Properties.UML_Property_Access;
The_Operation : AMF.UML.Operations.UML_Operation_Access;
end record;
Из подпрограмм обработки элементов дерева нам потребуется только одна, но объявить придётся обе, поэтому вторая будет описана как пустая:
procedure Pre_Operation
(Element : Asis.Element;
Control : in out Asis.Traverse_Control;
State : in out State_Information);
procedure Post_Operation
(Element : Asis.Element;
Control : in out Asis.Traverse_Control;
State : in out State_Information) is null;
Теперь остаётся настроить подпрограмму‐итератор на наш тип и подпрограммы:
procedure Iterate is
new Asis.Iterator.Traverse_Element (State_Information, Pre_Operation);
Реализация подпрограммы обработки элемента состоит из цепочки проверок вида текущего элемента и вызова кода обработки в необходимых случаях. В каждом случае создаётся UML объект необходимого типа и привязывается к UML элементам, созданным на предыдущем шаге:
procedure Pre_Operation
(Element : Asis.Element;
Control : in out Asis.Traverse_Control;
State : in out State_Information)
is
use type Asis.Declaration_Kinds;
begin
if Asis.Elements.Declaration_Kind (Element)
= Asis.A_Package_Declaration
then
declare
Elements : AMF.UML.Packageable_Elements.Collections.Set_Of_UML_Packageable_Element
:= State.The_Model.Get_Packaged_Element;
begin
State.The_Package := State.The_Factory.Create_Package;
Set_Name (State.The_Package, Element);
Elements.Add (State.The_Package);
end;
elsif Asis.Elements.Declaration_Kind (Element)
= Asis.An_Ordinary_Type_Declaration
then
declare
Elements : AMF.UML.Packageable_Elements.Collections.Set_Of_UML_Packageable_Element
:= State.The_Package.Get_Packaged_Element;
begin
State.The_Class := State.The_Factory.Create_Class;
Set_Name (State.The_Class, Element);
Elements.Add (State.The_Class);
end;
elsif Asis.Elements.Declaration_Kind (Element)
= Asis.A_Component_Declaration
then
declare
Attributes : AMF.UML.Properties.Collections.Ordered_Set_Of_UML_Property
:= State.The_Class.Get_Owned_Attribute;
begin
State.The_Property := State.The_Factory.Create_Property;
Set_Name (State.The_Property, Element);
Attributes.Add (State.The_Property);
end;
elsif Asis.Elements.Declaration_Kind (Element)
= Asis.A_Procedure_Declaration
then
declare
Operations : AMF.UML.Operations.Collections.Ordered_Set_Of_UML_Operation
:= State.The_Class.Get_Owned_Operation;
begin
State.The_Operation := State.The_Factory.Create_Operation;
Set_Name (State.The_Operation, Element);
Operations.Add (State.The_Operation);
end;
end if;
end Pre_Operation;
Подготовительные действия
Несмотря на всю простоту реализации необходимо ещё выполнить ряд подготовительных действий, которые не столь интуитивно понятны, поэтому остановимся на них подробнее.
Первое, что требуется выполнить это инициализировать ASIS и AMF. Процедура инициализации AMF выглядит достаточно просто:
with AMF.Internals.Modules.UML_Module;
pragma Unreferenced (AMF.Internals.Modules.UML_Module);
procedure Main is
begin
AMF.Facility.Initialize;
Далее необходимо создать хранилище для будущей модели:
declare
Store : AMF.URI_Stores.URI_Store_Access;
begin
Store :=
AMF.Facility.Create_URI_Store
(League.Strings.To_Universal_String ("file:///local"));
и получить фабрику UML объектов:
State.The_Factory :=
AMF.Factories.UML_Factories.UML_Factory_Access
(Store.Get_Factory
(League.Strings.To_Universal_String
("http://www.omg.org/spec/UML/20100901")));
и последнее подготовительное действие — создание объекта UML модели:
State.The_Model := State.The_Factory.Create_Model;
State.The_Model.Set_Name ((False, +"Example Model"));
Теперь можно инициализировать ASIS, загрузить дерево тестового примера и обработать его:
Asis.Implementation.Initialize ("-asis05");
Asis.Ada_Environments.Associate (Context, "A2U", "-C1 money.adt");
Asis.Ada_Environments.Open (Context);
Unit := Asis.Compilation_Units.Library_Unit_Declaration ("Money", Context);
A2U.Convert (Asis.Elements.Unit_Declaration (Unit));
И в заключении нужно вывести сформированную UML модель в XMI формат:
XMI.Writer (Store);
После выполнения программы получится XMI файл следующего содержания:
<?xml version="1.0"?>
<xmi:XMI xmlns:uml='http://www.omg.org/spec/UML/20100901' xmlns:xmi='http://www.omg.org/spec/XMI/20100901'>
<uml:Model xmi:type='uml:Model' xmi:id='16777217' name='Example Model'>
<packagedElement xmi:type='uml:Package' xmi:id='16777218' name='Money'>
<packagedElement xmi:type='uml:Class' xmi:id='16777219' name='Money'>
<ownedAttribute xmi:type='uml:Property' xmi:id='16777220' name='Amount'/>
<ownedOperation xmi:type='uml:Operation' xmi:id='16777221' name='Add'/>
</packagedElement>
</packagedElement>
</uml:Model>
</xmi:XMI>
Этот файл может быть прочитан редакторам UML, в которых возможно создать графическое представление.
Автор: Вадим Годунко Дата: 16.04.2012
|