Pzl-Component

Материал из wikiru.visual-prolog.com

Автор: Виктор Юхтенко

The necessary conditions to acknowledge the class as the pzl-component

Any class, which creates an objects (which has an interface), may be acknowledged as the component if:

  1. The name of the interface and the name of the class are different;
  2. The interface of the class supports the interface pzlComponent;
  3. The interface of the class includes the constants declarations:
    1. componentID_C of pzlDomains::entityUID_D type
    2. componentAlias_C of string type
    3. componentRunAble_C of core::booleanInt type
    4. componentMetaInfo_C of core::namedValue*. type
    5. сomponentDescriptor_C of pzlDomains::pzlComponentInfo_D type
  4. The declaration of the class contains the only constructor new(object ExistingObject);
  5. The implementation of the class derives the class pzlComponent;
  6. The package declarations file (.PH) of the class includes the file "System\Packs\pzlcomponent\pzlcomponent.ph".

The examples of the parts of the component declarations and implementations are shown below.

Class declaration

Contains the only constructor new:(object UsingObject). The name of the interface different from the name of the class.
class сomponentExample:iComponentExample
 
constructors
  new:(object UsingObject).
 
end class

Interface of the Class

Contains the qualifiyer supports pzlComponent and the declaration of constants
  • componentID_C
  • componentAlias_C
  • componentRunAble_C=b_True
  • componentMetaInfo_C
  • componentDescriptor_C
interface iComponentExample
  supports pzlComponent
 
open core
constants
  componentDescriptor_C:pzlDomains::pzlComponentInfo_D=pzlDomains::pzlComponentInfo
    (
    componentAlias_C,
    componentID_C,
    componentRunAble_C,
    componentMetaInfo_C
    ).
  componentID_C:pzlDomains::entityUID_D=pzlDomains::str("ComponentExample").
  componentAlias_C="ExampleOfTheComponent".
  componentRunAble_C=b_True.
  componentMetaInfo_C:namedValue_List=[].
 
predicates
  show:().
 
end interface iComponentExample

Class Implementation

Contains the inharitance statement inherits pzlComponent and also contains the clause for the constructor new(UsingObject)
implement componentExample
    inherits pzlComponent
 
clauses
  new(UsingObject):-
    ...
 
clauses
  show():-
  ...
end implement componentExample

The Package Header File ComponentExample.PH

Contains the include file 'PzlComponent.ph statement
#requires ...
#include @"System\Packs\pzlcomponent\pzlcomponent.ph"
 
#include @"pfc\core.ph"

Interface pzlComponent

The Interface pzlComponent corresponds to the class pzlComponent and this makes the class to be the pzl-component. The class PzlComponent , binds the component with other parts of the pzl-system. Also Interface PzlComponent helps in getting the information regarding the component.

The following predicates supported by the Pzl-system

  • getContainerVersion() - returns the information regarding the version of the container, where the given component placed
  • getContainerName() - returns the name of the file, where the given component placed
  • getClassInfo(className,classVersion) - returns the name and the version of the class pzlComponent

The other predicates of the interface PzlComponent:

  • getComponentVersion()-> string
  • getComponentID() -> entityUID_D
  • getComponentAlias() -> string
  • getComponentMetaInfo() -> namedValue*
  • release

must be defined in the implementation of the base class of the component. Otherwise the exception is generated while the appropriate call.

Predefined constants of the base interface of the pzl-component

The constant componentID_С

The constant сomponentID_С defines the identifier of the component, which must be unique if possible in the global sense. It means that two programs in the global information space (and we do not even mention one computer), which use the component with the same identifier, may be sure that they use exactly the same pzl-component. This may be reached by the using of the Microsoft company approach to identify MSCOM components. To fit this the pzl-technology uses the data structure with the functor uid:

uid(
  core::unsigned32 Unsigned,
  core::unsigned16 Short1,
  core::unsigned16 Short2,
  core::unsigned8 Byte1,
  core::unsigned8 Byte2,
  core::unsigned8 Byte3,
  core::unsigned8 Byte4,
  core::unsigned8 Byte5,
  core::unsigned8 Byte6,
  core::unsigned8 Byte7,
  core::unsigned8 Byte8).

and this is in reality the analogue of the structure core::nativeGuid of the Visual Prolog system. The identifier componentID_С used in two cases:

  • It is the identifier, which is used to register the component in the Windows registry or(and) in the user controlled component registry file
  • It is the identifier, which is used to register the component in the pzl-container

Because of the identifier in the form uid is combersome sequence of letters and figures, it is not convenient to use it while the learning of the pzl-technology.

To make the learning process easier, there is another way to represent the identifier - the string form str(string). Naturally, the uniqueness is not provided in that case, but for the educational purposes we may sacrifice the glibal uniqueness and to control the uniaueness of the identifier on the given computer only.

As the result we get two alternative forms to define identifier in the domain entityUID_D declaration:

entityUID_D=
  str(string);
  uid(unsined16,...).

Constant componentAlias_C

Together with the unique identifier, the pzl-technology gives the possbility to assign to a component the string name (Alias). To do so the name must be assigned to the constant ComponentAlias_C. The uniqueness of this name is not the hard demand.

You may create many text editors with the differenct features, which support the same interfaces. Each if these text editors will have the unique identifier of the entityUID_D type and possibly each of them will have it's own name of the base class. But all of them will have the same Alias (say "Text Editor". Then your applicaion (not knowing the name of the class), may use the name "Text Editor" while the creation of the instance of the Editor. The component, wich is registered at your computer this moment will be used to create an appropriate object. Changing the registration of the Editor you may use another Editor with the other features.

You must understand that all the collisions зкщмщсфеув by uniqueness of the aliases must be resolved by the programmer.

The Alias may be used by many tools of the pzl-technology to identify components on the screen or in the error messages.

Константа componentRunable_C

Эта константа сообщает о возможности запуска данной компоненты путем вызова предиката spbRun:(string UserInfo). Предикат spbRun декларируетс в одноименном интерфейсе spbRun.

Соответственно, компонента должна поддерживать интерфейс spbRun, если возможность вызова предиката spbRun предусматривается.

Если интерфейс spbRun компонентой поддерживается, то константа componentRunable_C должна иметь значение core::b_true и core::b_false - если не поддерживается.

Предикат spbRun - это, по крайней мере, один предикат в pzl-компоненте, который может этой компонентой поддерживаться и имя этого предиката известно.

Предикат spbRun имеет единственный входной параметр UserInfo типа string, назначение которого определяется создателем компоненты.

Если данная компонента может быть вызвана с помощью предиката spbRun, это означает, что в приложении для данной компоненты всегда имеются условия, которые позволяют этой компоненте выполнить какую-то функциональную задачу.

Это несколько напоминает функцию раздела GOAL - точка начала работы программы, но в случае с предикатом spbRun этот предикат может быть вызван, а может быть и не вызван.

Если приложение основано на использовании графического пользовательского интерфейса (pfc/GUI или pfc/VPI), то возможность запуска компоненты предикатом spbRun может означать, что будет создана какая-то форма на экране и эта форма далее позволит сделать что-то значимое (не обязательно полезное).

Если пользовательский интерфейс не используется, то предикат spbRun может быть использован и для других целей по усмотрению программиста.

Предикат spbRun:(string UserInfo) объявлен в интерфейсе spbRun, где он является единственным. Если компонента поддерживает интерфейс spbRun, то это декларируется стандартным способом:

interface componentInterface
  supports pzlComponent
  supports spbRun
 
constants
...
  ComponentRunable_C = b_true.
...
end interface componentInterface

либо один из интерфейсов, поддерживаемых базовым интерфейсом компоненты, должен поддерживать интерфейс spbRun, как показано ниже

interface componentInterface
  supports pzlComponent
  supports textEditor
 
constants
...
  componentRunable_C = b_true.
...
end interface componentInterface
 
interface textEditor
  supports spbRun
...
end interface textEditor

Константа componentMetaInfo_C

Константа componentMetaInfo_C имеет тип core::namedValue* и представляет список именованных данных, относящихся к данной компоненте - метаинформация о компоненте.

pzl-Система никак эту метаинформацию не обрабатывает, но при соотвествующем запросе к контейнеру данные этого списка доступны программисту.

Если метаинформация о компоненте отсутствует, то константа componentMetaInfo_C все равно должна быть определена и должна представлять пустой список.

constants
  componentMetaInfo_C = [].

Константа Описания Компоненты сomponentDescriptor_C

Константа сomponentDescriptor_C включает описания важнейших свойств компоненты перечисленные выше:

  • ComponentID_C,
  • ComponentAlias_C,
  • ComponentRunable_C
  • ComponentMetainfo_C

Константа сomponentDescriptor_C соответствует домену pzlDomains::pzlComponentInfo_D, объявленному в интерфейсном файле pzlDomains:

pzlComponentInfo_D=pzlComponentInfo
  (
  string Alias,
  entityUID_D ComponentID,
  booleanInt Runable,
  core::namedValue_List UserDefinedInfo
  ).

Пример описания констант pzl-компоненты

В реальной компоненте константы описания могут выглядеть так:

constants
  componentDescriptor_C:pzlDomains::pzlComponentInfo_D=pzlDomains::pzlComponentInfo
    (
    componentAlias_C,
    componentID_C,
    componentRunAble_C,
    componentMetaInfo_C
    ).
  componentID_C:pzlDomains::entityUID_D=pzlDomains::str("ComponentExample").
  componentAlias_C="ПримерКомпоненты".
  componentRunAble_C=b_True.
  componentMetaInfo_C:namedValue_List=[].

или так

constants
  componentDescriptor_C:pzlDomains::pzlComponentInfo_D=pzlDomains::pzlComponentInfo
    (
    componentAlias_C,
    componentID_C,
    componentRunAble_C,
    componentMetaInfo_C
    ).
  componentID_C:pzlDomains::entityUID_D=pzlDomains::uid(0x5701809E,0x0453,0x43A2,0xB1,0x82,0x8F,0xAE,0x2A,0x6B,0xA5,0x63).
  componentAlias_C="PzlAboutDialog".
  componentRunAble_C=b_False.
  componentMetaInfo_C:namedValue_List=
    [
    namedValue("CodeExporter",string("AboutExporter"))
    ].

Конструктор компоненты new:(object UsingObject)

Базовый класс pzl-Компоненты должен включать только один конструктор, имеющий формат new:(object UsingObject).

Здесь параметр UsingObject (что предполагает трактовку "С использованием объекта") не имеет никакого назначения и может быть использован автором pzl-Компоненты по своему усмотрению.

Например, в случае компоненты на базе GUI этот параметр может передавать объект типа window, соответствующий родительскому окну. Но если вызов конструктора в форме new(ParentWindow) автоматически преобразует тип window в тип object, то следует иметь в виду, что клауза конструктора, получит этот параметр именно как параметр типа object и непосредственное обращение к этому объекту невозможно.

Для обращения к объекту, переданному в виде параметра, необходимо привести параметр к типу, соответствующему возможному типу существующего объекта.

Например, в рассматриваемом случае с передачей объекта родительского окна для получения его высоты (height) надо привести объект ParentWindowAsObject в тип window:

clauses
  new(ParentWindowAsObject):-
    ParentWindow=convert(window,ParentWindowAsObject),
    WindowHeight=ParentWindow:getHeight(),
    ...

Особенности использования pzl-компонент как VIP-классов

Pzl-Компоненты, являясь обычными классами системы программирования Visual Prolog (VIP-классы), не требуют каких-либо специальных приемов программирования. Более того, при практическом программировании никакой разницы между использованием обычных VIP-классов и pzl-компонент нет. Тем не менее мы отметим здесь некоторые детали техники их созданиия и использования.

Создание pzl-компоненты

Создание pzl-компоненты возможно двумя способами:

Процедура создания pzl-компоненты стандартными средствами IDE не является рекомендуемой и приводится для разрешения коллизий в экстраординарных случаях

  • Создать в проекте, являющемся pzl-контейнером новый класс в пакете, который будет всегда связан с этим классом
  • При создании класса указать, что класс порождает объекты
  • После создания создания класса вручную модифицировать код для обеспечения всех ранее

перечисленных требований

Компиляция проекта, содержащего созданную таким образом pzl-компоненту, может привести к генерации множества ошибок, если не "зарегистрирована" в проекте-контейнере.

Для регистрации pzl-компоненты в pzl-контейнере необходимо:

  • В файле PzlConfig.i определить константу usePzl[ИмяКомпоненты]_C:boolean=true.
  • В файле PzlConfig.pack в раздел % privately used packages добавить полное имя заголовочного файла пакета
    #include @"ИмяФайлаКомпоненты.ph"
  • В файле PzlConfig.pro:
    • в список, возвращаемый предикатом getComponentIDInfo() добавить элемент вида
      [ИмяБазовогоИнтерфейсаКомпоненты]::componentDescriptor_C
    • добавить клаузу
new([ИмяБазовогоИнтерфейсаКомпоненты]::componentID_C,Container)=Object:-
  !,
  Object = [ИмяБазовогоКлассаКомпоненты]::new(Container)

.

Обращения к pzl-Компоненте

Существуют три способа вызова создания экземпляра pzl-компоненты:

  • путем вызова конструктора new(...)
  • путем вызова предиката newByName(...)
  • путем вызова предиката newByID(...)

Первый способ выглядит следующим образом:

... 
MyClassInstance =myClass::new(SomeObject), 
MyClassInstance:callNeededPredicate() 
...

Это соответствует обычным правилам VIP.

Второй способ использует pzl-систему явно и выглядит так:

...
MyClassObj=pzl::newByName(MyClass,...), 
MyClassInstance=tryConvert(iMyClass, MyClassObj), 
MyClassInstance:сallNeededPredicate(),

Здесь iMyClass является интерфейсом класса myClass, а “MyClass” является строковым именем класса (alias) myClass.

Третий способ также явно использует pzl-систему и выглядит так:

 
...
MyClassObj=pzl::newByID(str(MyClass),...), 
MyClassInstance=tryConvert(iMyClass, MyClassObj), 
MyClassInstance:сallNeededPredicate(),

Здесь iMyClass является интерфейсом класса myClass, а str(“MyClass”) является строковым представлением идентификатора класса.

Обращение к другим классам и компонентам

Обращение к другим классам и компонентам из текущей компоненты ничем не отличается от обычного стиля обращений к классам, объектам классов и объектам компонент. Следует иметь в виду, что для обращения к компоненте в стиле

...
ОбъектКомпоненты=классКомпоненты::new(SomeObject),
ОбъектКомпоненты:предикатКомпонеты(Аргумент1,...),
...

компонента классКомпоненты должна быть представлена в проекте, из которого производится это обращение, своим классом-представителем.

Однако, для обращения к компоненте в стиле

...
ОбъектКомпонентыObj=pzl::newByName("УсловноеИмяКомпоненты",Object),
ОбъектКомпоненты=convert(интерфейсКомпоненты,ОбъектКомпонентыObj),
ОбъектКомпоненты:предикатКомпонеты(Аргумент1,...),
...

или

...
ОбъектКомпонентыObj=pzl::newByID(ИдентификаторКомпоненты,Object),
ОбъектКомпоненты=convert(интерфейсКомпоненты,ОбъектКомпонентыObj),
ОбъектКомпоненты:предикатКомпонеты(Аргумент1,...),
...

класс-представитель компоненты не требуется.

Завершение использования

По завершении использования объекта компоненты все указатели на нее должны быть удалены. Такое стандартное для VIP обращение с объектами в случае pzl-Компоненты также приводит к гибели ее объекта.

Отладка

Если pzl-компонента находится в главном (исполняемом) приложении, то отладка текста ее праграммы ничем не отличается от приемов отладки обычного класса системы Visual Prolog.

Если pzl-компонента находится в pzl-Контейнере, представляющем собой DLL, то процесс отладки должен начинаться с проекта этого контейнера (DLL). Впрочем, это типичный подход к отладке DLL, принятый в системе Visual Prolog.

Регистрация

Если какой-либо класс и pzl-компонента находятся в одной и той же сущности (.EXE или та же самая DLL), и при этом обращение производится в форме

... 
MyClassInstance =myClass::new(SomeObject), 
MyClassInstance:callNeededPredicate() 
...

то регистрация pzl-Компоненты, к которой производится обращение, может не производиться.

Во всех остальных случаях компонента должна быть зарегистрирована

  • либо в локальном файле пользователя
  • либо в реестре LocalUser системы Windows (имя раздела HKEY_CURRENT_USER)
  • либо в реестре LocalMashine системы Windows (имя раздела HKEY_LOCAL_MACHINE)

Примечание. Для регистрации компонент следует использовать специальные средства pzl-Технологии. Регистрация компонент вручную не рекомендуется.

При регистрации компоненты в реестре системы Windows используются следующие имена директорий windows-реестра (указаны в файле VpPuZzle/System/Packs/pzlDomains.i)

constants
    registryVPPuZzleAtGlobal_C = "vp_PuZzle".
    registryComponentsAtGlobal_C = "vp_PuZzle\\pzlComponents".
 
    registryVPPuZzleAtLocalUser_C = "Software\\vp_PuZzle".
    registryComponentsAtLocalUser_C = "Software\\vp_PuZzle\\pzlComponents".
    registryRunAble_C = "RunAble".

При этом для именования свойств компонент используются константы

    registryRunAbleTrue_C="True".
    registryRunAbleFalse_C="False".

Получение информации о компоненте

Как уже упоминалось, следующие предикаты объявлены в интерфейсе pzlComponent и поддерживаются содержательно pzl-системой

  • getContainerVersion() - возвращает информацию о версии контейнера, в котором данная компонента содержится
  • getContainerName() - возвращает информацию об имени файла-контейнера, в котором содержится данная компонента
  • getClassInfo(className,classVersion) - возвращает информации об имени и версии класса pzlComponent

Для того, чтобы внешние по отношению к данной компоненте средства могли бы получить информацию об идентификаторе, условном наименовании и метаинформации, рекомендуется помещать следующие клаузы в базовый класс pzl-компоненты:

caluses
  getComponentID()= componentID_C.
  getComponentAlias() = componentAlias_C.
  getComponentMetaInfo() = componentMetaInfo_C

Напоминаем, что объявления этих предикатов содержатся в интерфейсе pzlComponent.

Ограничения в несанкционированном использовании

Pzl-Компонента не содержит никаких средств для ограничения ее использования. Если программа получает доступ к контейнеру, в котором расположена pzl-Компонента, и программе известны интерфейсы для общения с компонентной, то эта pzl-Компонента оказывается доступной программе.

Вы, как автор pzl-Компоненты, можете предусмотреть дополнительные меры по ограничению прав использования pzl-Компоненты.

Например, Вы можете предусмотреть сеанс идентификации прав, в ходе которого в pzl-Компоненту должна быть передана информация, подтверждающая права на использование данной компоненты.

Если текст программы pzl-Компоненты недоступен широкому кругу пользователей, то такого рода защита от несанкционированного использования может оказаться достаточно эффективной.

References