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.

Constant componentRunable_C

The constant componentRunable_C informs about the possibility for the given component to run independently by calling the predicate spbRun:(string UserInfo). The predicate spbRun is declared in the interface spbRun (file VpPuZzle/Interfaces/spbRun.i).

The component must support the interface spbRun, if the possibility to call the predicate spbRun expected.

If the component supports the interface spbRun then the constant componentRunable_C must have the value core::b_true otherwise it must have value core::b_false.

Predicate spbRun is the only one predicate, which may be supported by the component at least and the name of this predicate is known.

The predicate spbRun has the single input parameter UserInfo of string type. The meaning of the parameter may be defined by the developer of the pzl-component.

If the given component may be invoked using the predicate spbRun, then it means, that the applicaion has the conditions that give that component the possbility to run.

It is similar a little to the declaration GOAL - the initial point of the program. The only difference is that in case of the predicate spbRun it may be invoked or maybe not.

If the applicaion based on the User Interface (pfc/GUI или pfc/VPI), then the possibility to run using the predicate spbRun may lead to the creation of the form or the creation of the dialog, and this graphical entity will do something (not necessery useful).

If the user interface is not used, then the predicate spbRun may be used by the programmer for some other purposes.

The predicate spbRun:(string UserInfo) is declared in the interface spbRun, and it is the only predicate of that interface. If the component supports the interface spbRun, then it must be declared in the standard way:

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

I may be the case, when the one of the interfaces, which supported by the base component interface must support the interface spbRun as it is shown below

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

Constant componentMetaInfo_C

The constant componentMetaInfo_C belongs to the type core::namedValue* and it may contain the list of named values, which have the sense, relative to the given component (meta information regarding the component).

Pzl-system does nothing with this information. This information may be requested from the container and from the component by programmer.

If there is no metainformation regarding the component, the constant componentMetaInfo_C must be defined anyway (as the empty list).

constants
  componentMetaInfo_C = [].

Constant сomponentDescriptor_C

The constant сomponentDescriptor_C integrates the most important features of the component defined by the constants listed above:

  • ComponentID_C,
  • ComponentAlias_C,
  • ComponentRunable_C
  • ComponentMetainfo_C

The constant сomponentDescriptor_C corresponds to the domain pzlDomains::pzlComponentInfo_D, declared in in the interface pzlDomains (the file VpPuZzle/Domains/pzlDomains.i):

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

The pzl-component description example

In the real component the constants describing the component may ook like shown below:

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=[].

or it may look like

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