System/Docu/ru/Stores.odc

Stores
ENGLISH
Частичная редакция перевода: Ф.В.Ткачев, 2010-10-06. Отредактирована часть, касающаяся записи/чтения элементарных типов Компонентного Паскаля, что нужно в первую очередь для приложений общего характера, неспецифичных для аппарата составных документов Блэкбокса. Неотредактированный кусок отмечен.

DEFINITION Stores;

	IMPORT Files;

	CONST
		alienVersion = 1; alienComponent = 2;
		inconsistentVersion = -1; inconsistentType = -2; moduleFileNotFound = -3;
		invalidModuleFile = -4; inconsModuleVersion = -5; typeNotFound = -6;

	TYPE
		TypeName = ARRAY 64 OF CHAR;
		TypePath = ARRAY 16 OF TypeName;

		OpName = ARRAY 32 OF CHAR;

		Domain = POINTER TO LIMITED RECORD
			PROCEDURE (d: Domain) GetSequencer (): ANYPTR, NEW;
			PROCEDURE (d: Domain) SetSequencer (sequencer: ANYPTR), NEW
		END;

		Operation = POINTER TO ABSTRACT RECORD
			PROCEDURE (op: Operation) Do, NEW, ABSTRACT
		END;

		Store = POINTER TO ABSTRACT RECORD
			PROCEDURE (s: Store) Domain (): Domain, NEW;
			PROCEDURE (s: Store) CopyFrom- (source: Store), NEW, EMPTY;
			PROCEDURE (s: Store) Internalize- (VAR rd: Reader), NEW, EMPTY;
			PROCEDURE (s: Store) Externalize- (VAR wr: Writer), NEW, EMPTY;
			PROCEDURE (s: Store) ExternalizeAs- (VAR s1: Store), NEW, EMPTY
		END;

		Reader = RECORD
			rider-: Files.Reader;
			cancelled-: BOOLEAN;
			readAlien-: BOOLEAN;
			(VAR rd: Reader) ConnectTo (f: Files.File), NEW;
			(VAR rd: Reader) Pos (): INTEGER, NEW;
			(VAR rd: Reader) SetPos (pos: INTEGER), NEW;
			(VAR rd: Reader) ReadBool (OUT x: BOOLEAN), NEW;
			(VAR rd: Reader) ReadSChar (OUT x: SHORTCHAR), NEW;
			(VAR rd: Reader) ReadXChar (OUT x: CHAR), NEW;
			(VAR rd: Reader) ReadChar (OUT x: CHAR), NEW;
			(VAR rd: Reader) ReadByte (OUT x: BYTE), NEW;
			(VAR rd: Reader) ReadSInt (OUT x: SHORTINT), NEW;
			(VAR rd: Reader) ReadXInt (OUT x: INTEGER), NEW;
			(VAR rd: Reader) ReadInt (OUT x: INTEGER), NEW;
			(VAR rd: Reader) ReadLong (OUT x: LONGINT), NEW;
			(VAR rd: Reader) ReadSReal (OUT x: SHORTREAL), NEW;
			(VAR rd: Reader) ReadXReal (OUT x: REAL), NEW;
			(VAR rd: Reader) ReadReal (OUT x: REAL), NEW;
			(VAR rd: Reader) ReadSet (OUT x: SET), NEW;
			(VAR rd: Reader) ReadSString (OUT x: ARRAY OF SHORTCHAR), NEW;
			(VAR rd: Reader) ReadXString (OUT x: ARRAY OF CHAR), NEW;
			(VAR rd: Reader) ReadString (OUT x: ARRAY OF CHAR), NEW;
			(VAR rd: Reader) ReadStore (OUT x: Store), NEW;
			(VAR rd: Reader) ReadVersion (min, max: INTEGER; OUT version: INTEGER), NEW;
			(VAR rd: Reader) TurnIntoAlien (cause: INTEGER), NEW
		END;

		Writer = RECORD
			rider-: Files.Writer;
			writtenStore-: Store;
			(VAR wr: Writer) ConnectTo (f: Files.File), NEW;
			(VAR wr: Writer) Pos (): INTEGER, NEW;
			(VAR wr: Writer) SetPos (pos: INTEGER), NEW;
			(VAR wr: Writer) WriteBool (x: BOOLEAN), NEW;
			(VAR wr: Writer) WriteSChar (x: SHORTCHAR), NEW;
			(VAR wr: Writer) WriteXChar (x: CHAR), NEW;
			(VAR wr: Writer) WriteChar (x: CHAR), NEW;
			(VAR wr: Writer) WriteByte (x: BYTE), NEW;
			(VAR wr: Writer) WriteSInt (x: SHORTINT), NEW;
			(VAR wr: Writer) WriteXInt (x: INTEGER), NEW;
			(VAR wr: Writer) WriteInt (x: INTEGER), NEW;
			(VAR wr: Writer) WriteLong (x: LONGINT), NEW;
			(VAR wr: Writer) WriteSReal (x: SHORTREAL), NEW;
			(VAR wr: Writer) WriteXReal (x: REAL), NEW;
			(VAR wr: Writer) WriteReal (x: REAL), NEW;
			(VAR wr: Writer) WriteSet (x: SET), NEW;
			(VAR wr: Writer) WriteSString (IN x: ARRAY OF SHORTCHAR), NEW;
			(VAR wr: Writer) WriteXString (IN x: ARRAY OF CHAR), NEW;
			(VAR wr: Writer) WriteString (IN x: ARRAY OF CHAR), NEW;
			(VAR wr: Writer) WriteStore (x: Store), NEW;
			(VAR wr: Writer) WriteVersion (version: INTEGER), NEW
		END;

		AlienComp = POINTER TO LIMITED RECORD
			next-: AlienComp
		END;

		AlienPiece = POINTER TO LIMITED RECORD (AlienComp)
			next-: AlienComp;
			pos-, len-: INTEGER
		END;

		AlienPart = POINTER TO LIMITED RECORD (AlienComp)
			next-: AlienComp;
			store-: Store
		END;

		Alien = POINTER TO LIMITED RECORD (Store)
			path-: TypePath;
			cause-: INTEGER;
			file-: Files.File;
			comps-: AlienComp
		END;

	PROCEDURE InitDomain (s: Store);
	PROCEDURE CopyOf (s: Store): Store;
	PROCEDURE Join (s0, s1: Store);
	PROCEDURE Joined (s0, s1: Store): BOOLEAN;
	PROCEDURE Unattached (s: Store): BOOLEAN;
	PROCEDURE ExternalizeProxy (s: Store): Store;
	PROCEDURE Report (IN msg, p0, p1, p2: ARRAY OF CHAR);

END Stores.

Введение

Модуль Stores определяет тип данных Store, котоый следует использовать как базовый тип для всех сохраняемых переопределяемых (расширяемых, extensible) объектов. Когда сохраняется объект, тип которого допускает переопределения, необходимо правильно сохранить не только содержимое объекта, но и его реальный тип. Сохранять тип нужно для воссоздания объекта правильного типа при повторном считывании данных.
Объект хранения (store; речь идёт об объекте конкретного типа, переопределяющего абстрактный тип Store прим. Ф.Т.) может сохраняться в файл. Объект хранения должен реализовать процедуру Internalize (загрузить), которая требует объект чтения Reader в качестве параметра, и процедуру Externalize (выгрузить), которая в качестве параметра требует объект записи Writer. Эти процедуры считывают/записывают сохраняемое состояние объекта. (Объект хранения может также иметь временные компоненты состояния, которые не сохраняются.) Объекты чтения (Reader) и записи (Writer) занимаются прямым и обратным преобразованием данных в последовательности байтов, записываемые в файлы. Примеры переопределений типа Store  типы Views.View и TextModels.Model.

Объекты хранения могут образовывать произвольные графы. Граница графа определена его доменом (domain). Это объект, который представляет всю совокупность объектов хранения, которые могут быть выгружены в файл или загружены из файла как единое целое. Объекты хранения, принадлежащие одному домену, загружаются и выгружаются так, что связывающие их между собой указатели корректно восстанавливаются при загрузке. В частности, корректно обрабатываются множественные указатели: если несколько объектов хранения из одного домена указывают на другой элемент своего домена, то этот элемент считывается в память один раз, и все указатели на него корректно восстанавливаются. При этом корректно обрабатываются произвольные графи, например, циклические списки. Ссылки на объекты хранения из других доменов запрещены.

Документ состоит из иерархии объектов изображения и моделей, которые все суть объекты хранения. В документе могут содержаться и другие объекты хранения, например, атрибуты текстов или объекты-контроллеры. Все объекты хранения документа являются частью одного домена.
Объектами хранения в домене можно управлять косвенно посредством объектов операций. Объект операции реализует метод Do, который должен быть обратным к самому себе (т.е. его второе выполнение должно отменять эффект первого). Это ключ к механизму отмены/повтора (undo/redo) в архитектуре составных документов Блэкбокса. Однако в принципе этот механизм можно использовать для реализации транзакций на хранимых данных, не являющихся документами, но представленных объектами хранения.

Содержимое объекта хранения хранится в файле. При чтении этого файла другой конфигурацией Блэкбокса, может оказаться, что не все необходимые модули доступны в этой конфигурации, т.е. модуль, в котором определён конкретный тип объекта хранения, не может быть загружен. Тем не менее, чтение неопознанного (alien) объекта хранения не вызовет ошибки. Вместо истинного типа объекта генерируется тип неопознанного объекта. Очевидно, что такой объект не может интерпретировать данные, которые он представляет, и поэтому не может обладать особым поведением. Однако, он может быть скопирован и сохранён в другой файл так, что его содержимое в новом файле останется нетронутым и готовым к использованию в случае его опознания.

<начало неотредактированного куска>
Для того, чтобы определить множество хранилищ, которые подходят домену и установить текущий домен, есть две процедуры, предоставляемые средой. Первая процедура нужна для сборки хранилищ во множества и их соединения, а вторая используется для доступа  текущего домена ко множеству хранилищ. В BlackBox это процедуры Join и InitDomain. Более детально они обсуждаются ниже.

Join

Процедура Join используется для соединения двух множеств хранилищ. Эта процедура будет вызываться всякий раз тогда, когда ссылку из хранилища в одном множестве устанавливают на хранилище в другом множестве. Его аргументами, обычно, должны быть два множества хранилища. Join симметрична. Если два хранилища, которые являеются аргументами, уже связаны, Join не даст эффекта. Рисунок 1 демонстрирует результат работы операции Join на двух множествах хранилищ X и Y.


Рисунок 1: Эффект работы операции Join на двух несвязанных множествах хранилищ X и Y.

Вызовы Join должны быть добавлены  в тех местах, где вновь созданные хранилища соединены, обычно в процедуре-фабрике. Все эти хранилища должны быть соединены с хранилищем s, которое записано на диск в методе Externalize хранилища s. 

Вызовы Join не нужны в методах Internalize и CopyOf, где обычно также установлены ссылки между хранилищами. Модуль Stores имеет достаточно информации для того, чтобы эти хранилища могли соединяться.

Функция Joined позволяет проверить, которое из двух данных хранилищ соответствует такому же, т.е., были ли они соединены или нет. Joined симметрична, рефлексивна и транзитивна.

InitDomain

Для дотого, чтобы правильно сопоставить текущий домен пустому хранилищу или множеству хранилищ, вызывается процедура InitDomain. В качестве параметра используется член множества (или единичное хранилище). Текущий домен создается внутри модуля Stores (Domain типа limited) и тогда назначается всем хранилищам во множестве. После того, как домен был назначен графу множества, к нему мождно получить доступ с помощью метода Domain(), определенного в типе Store. Если вызывается InitDomain на хранилище, которое уже назначено домену, то процедура не будет иметь эффекта. Среда вызывает InitDomain в модуле Documents только после создания нового документа. Обычно, программистам BlackBox нет необходимости вызывать InitDomain самостоятельно, исключая случаи, когда создается детальная копия документа.

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

Функция Joined может быть применена к хранилищам независимо от того, привязаны ли они к домену или нет. Если хранилище s0 или s1 привязано к домену, то Joined(s0, s1) эквивалентно s0.Domain() = s1.Domain().

Процедуры Join и InitDomain определяют следующие пред- и постусловия.

	PROCEDURE Join (s0, s1: Store)
		PRE 20	s0 # NIL
		PRE 21	s1 # NIL
		PRE 22	s0.Domain() = NIL OR s1.Domain() = NIL OR s0.Domain() = s1.Domain()
		POST	Joined(s0, s1)
	
	PROCEDURE InitDomain (s: Store)
		PRE 20	s # NIL
		POST	s.Domain() # NIL
	
Если два хранилища должны быть соединены и предусловие 22 не встречается, то детальная копия двух хранилищ будет генерироваться при помощи CopyOf.

Висячие указатели и копирование

Хранилища домена выгружены и загружены, в процессе загрузки указатели между ними восстанавливаются корректно. В частности, висячие указатели обрабатываются корректно: если несколько хранилищ указывают на некий элемент того же домена, этот элемент считается однажды, и все указатели на него восстановятся. Случайные графы обрабатываются аналогично, включая циклические структуры данных.

Возможность клонирования хранилищ предоставляется процедурой CopyOf. Эта процедура создает новый объект и затем вызывает метод CopyFrom с исходным объектом в качестве параметра. Этот механизм может быть средством  записи хранилища в файл и чтения его обратно в качестве клона.

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

Однако, может возникнуть проблема с восстановлением висячих указателей если во время вызова CopyOf дополнительные хранилища будут добавлены ко множеству хранилищ, которое содержит информацию, утратившую свою актуальность. Поэтому, Join(s0, s1) не должна быть вызвана, пока операция CopyOf активна на одном из хранилищ s0 или s1. Если появилась такая ситуация, то выполнение порордит исключение. Этого можно избежать соединением содержащих себя хранилищ до копирования графа хранилища.

Дополнительные замечания

Нелегалы

Нелегалы обрабатываются особым путем относительно доменов. После того, как они становятся неизменными, они никогда не будут скопированы и не смогут быть вставлены в любой граф. Хранилище ссылается только на один домен. Это может быть нарушено тем правилом, что хранилище в документе может не иметь отношения к хранилищам которые соответствуют другому домену. Поэтому, все нелегалы назначены специальному домену нелегалов. Модуль Stores "знает" это правило и не порождает исключения, если нелегал выгружается.

Операции и undo

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

Упрощенные копии

Модуль Views предлагает процедуру CopyOf, которая позволяет делать упрощенные копии отображений. Упрощенная копия отображения подразумевает, что копия относится к той же модели, что и оригинал. Как модели и отображения всегда соединены, так предполагается, что и отображение и его упрощенная копия соединены.

Проверка множества хранилищ

Множество хранилищ не может быть проверено программно более, чем это предлагает процедура Joined. Однако, отладчик позволяет производить итерации над всеми хранилищами во множестве хранилищ. Хранилища, которые принадлежат одному и тому же множеству связаны в циркулярный лист. Как только домен становится ассоциированным с множеством, ссылки устанавливаются в NIL и каждое хранилище во множестве обращается к новому домену. Для произведения итераций над множеством хранилищ, отладчик должен быть вызван до того, как множество хранилищ будет связано. Тогда хранилища могут быть итерированы с помощью поля next хранилища. Листинг 2 показывает простую процедуру, которая генерирует пустой текст отображения и после этого открывает отладчик. Следуя полям next отображения v, вы увидите, что следующие семь хранилищ соединены в одно множество: TextModels.Model, TextModels.Attributes, TextRulers.Ruler, TextRulers.Style, TextRulers.Attributes, TextViews.View and TextControllers.Controller.

MODULE Test;

	IMPORT TextViews, TextModels;
	
	PROCEDURE Do*;
		VAR v: TextViews.View;
	BEGIN
		v := TextViews.dir.New(TextModels.dir.New());
		HALT(0);
	END Do;
	
END Test.

Листинг 2: Проверка хранилищ во множестве хранилищ

Не привязанные

Функция Unattached(s) проверяет было ли хранилище s присоединено к другим хранилищам или было привязано к домену. Если не выполняется ни одно условие, ни другое, Unattached возвращает TRUE. Только средой решается может ли одиночное хранилище быть просто соединено с другим графом хранилища или должна генерироваться детальная копия; например, для хранилища, которое сохранено в кэше и которое может быть соединено с другими графами хранилищ.

Для этого используется следующий участок кода. В этом примере, хранилище s1 копируется, если оно связано. This way an unnecessary copy call in the case that the store is unattached is prevented.

	IF ~Stores.Joined(s0, s1) THEN
		IF ~Stores.Unattached(s1) THEN s1 := Stores.CopyOf(s1) END;
		Stores.Join(s0, s1)
	END;

Код не симметричен, поскольку ему часто статически известно, что любое из двух хранилищ s0 или s1 никогда не будет скопировано. Это используется для малых хранилищ, которые генерируются однажды и могут быть соединены различными множествами хранилищ.

Типы данных

Stores предоставляет пары проекционных типов, которые используются в качестве параметров процедурах Internalize и Externalize хранилищ. Эти считыватели/записыватели используют следующий внешний (прямой порядок передачи данных) формат:

BOOLEAN
	1 байт (0 = FALSE, 1 = TRUE)

SHORTCHAR
	1 байт в кодировке Latin-1 (т.е., Юникод страница 0; 00X..0FFX)

CHAR
	2 байта в кодировке Unicode (0000X..0FFFFX)

BYTE
	1 байт (-128..127)

SHORTINT
	2 байта (-32768..32767)

INTEGER
	4 байта (-2147483648..2147483647)

LONGINT
	8 байт (-9223372036854775808..9223372036854775807)

SHORTREAL
	4 байта в формате IEEE 

REAL
	8 байт в формате IEEE 

SET
	4 байта (последний проверочный бит = элемент 0)

String
	строка в кодировке Latin-1, за которой следует 00X

Long String
	строка в кодировке Unicode, за которой следует 0000X


CONST alienVersion
Это значение сопоставляется полю cause считывателя, если Reader.ReadVersion считает версию вне определенного диапазона.

CONST alienComponent
Это значение может быть использовано в качестве cause параметра в Reader.TurnIntoAlien для указания, что хранилище может прочитаться, но что некоторые хранилища, содержащиеся в нем, являются чужеродными. Например, отображение может стать нелегалом если его модель чужеродна.

CONST inconsistentVersion
Это значение сопоставляется полю считывателя cause если Reader.ReadVersion считывает блок данных, который имеет длину, не соответствующую его содержимому, т.е., не все данные могут быть прочитаны, или была попытка произвести чтение после окончания данных.

CONST inconsistentType
Это значение сопоставляется полю считывателя cause если Reader.ReadVersion обнаружил изменение в типе расширения иерархии загруженного типа.

CONST moduleFileNotFound
Это значение сопоставляется полю считывателя cause если Reader.ReadVersion пытался загрузить модуль, определенный как internalized, и файл кода для этого модуля не может быть найден.

CONST invalidModuleFile
Это значение сопоставляется полю считывателя cause если Reader.ReadVersion пытался загрузить модуль, определенный как internalized, и модуль не смог быть загружен, поскольку он импортирует другой модуль, который не может быть загружен по каким-либо причинам.

CONST inconsModuleVersion
Это значение сопоставляется полю считывателя cause если Reader.ReadVersion пытался загрузить модуль, определенный как internalized, и модуль не смог быть загружен, поскольку его версия несовместима с некоторыми уже загруженными модулями.

CONST typeNotFound
Это значение сопоставляется полю считывателя cause если Reader.ReadVersion пытался загрузить не созданный тип (однако, модуль был найден).


TYPE TypeName
Строковый тип для имени типа объекта.


TYPE TypePath
Массив имен типа.


TYPE OpName
Строковый тип для имени операции.


TYPE Domain
LIMITED
Домен представляет собой граф хранилищ, которые целиком могут быть сохранены в файл. Все хранилища домена ссылаются на объект типа Domain.

PROCEDURE (d: Domain) GetSequencer (): ANYPTR
NEW
Для внутреннего использования.

PROCEDURE (d: Domain) SetSequencer (sequencer: ANYPTR)
NEW
Для внутреннего использования.


TYPE Operation
ABSTRACT
Операция - это объект, который представляет собой команду, выполненную над некоторыми хранилищами домена (над одним хранилище). Операция может быть отменена и повторена.

PROCEDURE (op: Operation) Do
NEW, ABSTRACT
Этот метод реализует поведение при эксплуатации операции. Он должен быть авто-инверсным, т.е., если выполнился четное число раз, то не должен иметь эффекта. Если же нечетное число раз, то эффект должен быть таким же, как при выполнении единожды.


TYPE Store
ABSTRACT
Расширяемые типы данных, способные сохранять свое состояние, такие как Views.View или TextModels.Text - являются расширениями Store.
Хранилища типично размещены подходящими директориями, например, Views.Directory или TextModels.Directory.
Хранилища используются как базовый тип для всех объектов, оба из которых должны быть расширяемыми и персистентными.

PROCEDURE (s: Store) Domain (): Domain
NEW
Хранилище может быть ассоциировано с доменом. Это выполняется процедурой InitDomain, которая назначает домен хранилищу.
Domain может быть вызван случайными клиентами.

PROCEDURE (s: Store) CopyFrom- (source: Store)
NEW, EMPTY
Копировать содержания source в s. Создается детальная копия.

Pre
source # NIL	
TYP(source) = TYP(s)	
s.Domain() = NIL	
s еще не инициализировано

PROCEDURE (s: Store) Internalize- (VAR rd: Reader)
NEW, EMPTY
(Для обратной совместимости, этот метод фактически еще EXTENSIBLE. Это может измениться в новых версиях BB.)
Читает содержание s из считывателя rd. Internalize должен читать данные (размер данных), которые были записаны в соответствии с процедурой Externalize.
Internalize вызывается локально.
Internalize расширена различными типами персистентных объектов, например, моделями, отображениями, и контроллерами.

Pre
source.Domain() = NIL	
source еще не инициализировано	

PROCEDURE (s: Store) Externalize- (VAR wr: Writer)
NEW, EMPTY
(Для обратной совместимости, этот метод фактически еще EXTENSIBLE. Это может измениться в новых версиях BB.)
Пишет содержимое s в записыватель wr. Externalize должен писать данные (размер данных), которые были считаны в соответствии с процедурой Internalize.
Externalize вызывается локально.
Externalize расширяется различными типами персистентных объектов, например, моделями, отображениями, и контроллерами.

PROCEDURE (s: Store) ExternalizeAs- (VAR s1: Store)
NEW, EMPTY
До вызова процедуры Externalize, вызывается процедура ExternalizeAs, которая дает возможность хранилищу указать на другое хранилище, которое должно быть выгружено на свое место ("прокси"). Также возможно установить s1 в NIL, это подразумевает, что хранилище не будет выгружено полностью. Это используют, например, для маркеров компилятора ошибок, которые никогда не сохранялись.
ExternalizeAs вызывается локально.

Pre
s1 = s	
<конец неотредактированного куска>


TYPE Reader
Объект чтения величин, определённых в Компонентном Паскале, таких как целые, вещественные, или множества. 
Содержит Files.Reader, который он использует для большинства опреаций.
Объекты чтения используются в процедуре Store.Internalize.
Объекты чтения не допускают переопределения (not extensible).

rider-: Files.Reader
Файловый бегунок, который связывает объект чтения с файлом.

cancelled-: BOOLEAN	 <отменено> флажок имеет смысл в процедуре Store.Internalize
Сообщает о том, была ли текущая активация Internalize вызвана процедурами ReadVersion или TurnIntoAlien.

readAlien-: BOOLEAN
Сообщает о том, был ли считан какой-нибудь неопознанный объект (alien) с момента последнего вызова ConnectTo.

PROCEDURE (VAR rd: Reader) ConnectTo (f: Files.File)
NEW
Подсоединяет объект чтения к файлу. Все последующие операции требуют, чтобы объект чтения был подсоединён, т.е. rd.rider # NIL. Однако это предусловие явно не проверяется. Позиция объекта чтения после подсоединения  в начале файла. Если тот же самый объект чтения нужно использовать для другого файла, сначала его следует закрыть, подсоединяя к NIL.
ConnectTo используется внутри каркаса Блэкбокса. (Смысл этой фразы в том, что в приложении достаточно определить для своих объектов методы-хуки вроде Internalize/Externalize и загружать документы командами высокого уровня вроде Views.OldView, и тогда не возникает нужды явно иметь дело с этой процедурой.прим. Ф.Т.)

Предусловие
20	(f = NIL) OR (rd.rider = NIL)

Постусловие
f = NIL
	rd.rider = NIL
f # NIL
	(rd.rider # NIL) & (rd.rider.Base() = f)
	rd.Pos() = 0

PROCEDURE (VAR rd: Reader) Pos (): INTEGER
NEW
Возвращает текущую позицию объекта чтения.

Постусловие
0 <= result <= rd.rider.Base().Length()

PROCEDURE (VAR rd: Reader) SetPos (pos: INTEGER)
NEW
Устанавливает текущую позицию объекта чтения.

Предусловие
20	pos >= 0
21	pos <= rd.rider.Base().Length()

Постусловие
rd.Pos() = pos
~rd.rider.eof

PROCEDURE (VAR rd: Reader) ReadBool (OUT x: BOOLEAN)
NEW
Читает булевское значение.

PROCEDURE (VAR rd: Reader) ReadSChar (OUT x: SHORTCHAR)
NEW
Читает короткую литеру (00X..0FFX).

PROCEDURE (VAR rd: Reader) ReadXChar (OUT x: CHAR)
NEW
То же, что и ReadSChar, но принимающий параметр типа CHAR.
Эта процедура предоставлена для упрощения перехода с версии 1.2 на 1.3.

PROCEDURE (VAR rd: Reader) ReadChar (OUT x: CHAR)
NEW
Читает литеру (0000X..0FFFFX).

PROCEDURE (VAR rd: Reader) ReadByte (OUT x: BYTE)
NEW
Читает очень короткое целое (-128..127).

PROCEDURE (VAR rd: Reader) ReadSInt (OUT x: SHORTINT)
NEW
Читает короткое целое (-32768..32767).

PROCEDURE (VAR rd: Reader) ReadXInt (OUT x: INTEGER)
NEW
То же, что и ReadSInt, но принимающий параметр типа INTEGER.
Эта процедура предоставлена для упрощения перехода с версии 1.2 на 1.3.

PROCEDURE (VAR rd: Reader) ReadInt (OUT x: INTEGER)
NEW
Читает целое (-2147483648..2147483647).

PROCEDURE (VAR rd: Reader) ReadLong (OUT x: LONGINT)
NEW
Читает длинное целое (-9223372036854775808..9223372036854775807).

PROCEDURE (VAR rd: Reader) ReadSReal (OUT x: SHORTREAL)
NEW
Читает короткое вещественное (32-битное число в формате IEEE).

PROCEDURE (VAR rd: Reader) ReadXReal (OUT x: REAL)
NEW
То же, что и ReadSReal, но принимающий параметр типа REAL.
Эта процедура предоставлена для упрощения перехода с версии 1.2 на 1.3.

PROCEDURE (VAR rd: Reader) ReadReal (OUT x: REAL)
NEW
Читает вещественное число (64-битное число в формате IEEE).

PROCEDURE (VAR rd: Reader) ReadSet (OUT x: SET)
NEW
Читает множество (значение типа SET, 32 элемента).

PROCEDURE (VAR rd: Reader) ReadSString (OUT x: ARRAY OF SHORTCHAR)
NEW
Читает цепочку из коротких (однобайтных) литер, оканчивающуюся 0X.

Предусловие
LEN(x) больше, чем длина считываемой цепочки

PROCEDURE (VAR rd: Reader) ReadXString (OUT x: ARRAY OF CHAR)
NEW
То же, что и ReadSString, но принимающий параметр массив литер CHAR..
Эта процедура предоставлена для упрощения перехода с версии 1.2 на 1.3.

PROCEDURE (VAR rd: Reader) ReadString (OUT x: ARRAY OF CHAR)
NEW
Читает цепочку литер, оканчивающуюся нулем.

Предусловие
LEN(x) больше, чем длина считываемой цепочки

PROCEDURE (VAR rd: Reader) ReadStore (OUT x: Store)
NEW
Читает тип объекта хранения, размещает объект в памяти, и затем загружает его содержимое, вызывая его процедуру Internalize. В результате значение x может быть равным NIL, а также может оказаться неопознанным объектом, если соответствующий модуль не может быть загружен, или если загрузка была отменена самой процедурой Internalize.
Если объект записи уже был считан, то возвращается указатель вместо размещения нового объекта. Это подразумевает, что произвольные графы, которые были записаны WriteStore, восстанавливаются корректно, включая висячие указатели на такие хранилища, циклы, и т.д.
Если файл с которым считыватель работает, не содержит корректных данных, произойдёт останов по ASSERT (с n=101...106).

Предусловие
20	объект чтения стоит в начальной позиции нового объекта хранения

Постусловие
в файле пустой объект хранения
	x = NIL
в файле непустой объект хранения
	x # NIL
		x IS Alien
			x.cause # 0
			x.type # ""
			x.file # NIL
			x.pos >= 0	начало данных объекта
			x.len >= 0	длина данных объекта
			неопознанный объект содержится в x.file в диапазоне [x.pos .. x.pos + x.len[.
			Эти данные включают в себя только содержимое объекта хранения, но не его префикс
		~(x IS Alien)
			x был успешно прочтен

PROCEDURE (VAR rd: Reader) ReadVersion (min, max: INTEGER; OUT version: INTEGER)
NEW
Читает байт версии и возвращает его в параметре version. Если version не попадает в range [min .. max], считываемый объект хранения превращается в неопознанный объект, при этом cause = alienVersion.

Предусловие
20	0 <= min <= max

Постусловие
min <= version <= max
	правильная версия
(version < min) OR (version > max)
	неправильная версия
	rd.cause = alienVersion
	rd.cancelled
	rd.readAlien

PROCEDURE (VAR rd: Reader) TurnIntoAlien (cause: INTEGER)
NEW
Загружаемый объект хранения может превратиться в неопознанный объект, например если он загрузил объект, оказавшийся неопознанным, в качестве сввоей компоненты.

Предусловие
20	cause > 0


TYPE Writer
Объект записи значений Компонентного Паскаля таких, как целые, вещественные или множества. 
Объект записи содержит Files.Writer, к которому он обращается в большинстве своих операций.
Объекты записи используются в процедуре Externalize.
Объекты записи не допускают переопределения.

rider-: Files.Writer
Файловый бегунок, который соединяет объект записи с файлом.

writtenStore-: Store
Объект хранения, который был записан последним в результате вызова процедуры WriteStore.

PROCEDURE (VAR wr: Writer) ConnectTo (f: Files.File)
NEW
Подсоединить объект записи к файлу. Все описываемые ниже операции требуют, чтобы объект записи был подсоединён, т.е. чтобы wr.rider # NIL. Однако это предусловие явно не проверяется. 
Позиция объекта записи после подсоединения  в конце файла. Если такой объект необходимо повторно использовать с другим файлом, он должен быть сначала закрыт подсоединением к NIL.
ConnectTo используется внутри каркаса Блэкбокса. (См. примечание к аналогичному методу типа Reader. прим. Ф.Т.)

Предусловие
20	(f = NIL) OR (wr.rider = NIL)

Постусловие
f = NIL
	wr.rider = NIL
f # NIL
	wr.rider # NIL  &  wr.rider.Base() = f
	wr.Pos() = wr.rider.Base().Length()

PROCEDURE (VAR wr: Writer) Pos (): INTEGER
NEW
Возвращает текущую позицию объекта записи.

Постусловие
0 <= result <= wr.rider.Base().Length()

PROCEDURE (VAR wr: Writer) SetPos (pos: INTEGER)
NEW
Устанавливает текущую позицию объекта записи в pos.

Предусловие
20	pos >= 0
21	pos <= wr.rider.Base().Length()

Постусловие
wr.Pos() = pos

PROCEDURE (VAR wr: Writer) WriteBool (x: BOOLEAN)
NEW
Записывает булевское значение.

PROCEDURE (VAR wr: Writer) WriteSChar (x: SHORTCHAR)
NEW
Записывает короткую литеру (00X..0FFX).

PROCEDURE (VAR wr: Writer) WriteXChar (x: CHAR)
NEW
То же, что и WriteSChar, но имеет параметр типа CHAR.
Эта процедура предоставлена для упрощения перехода с версии 1.2 на 1.3.

PROCEDURE (VAR wr: Writer) WriteChar (x: CHAR)
NEW
Записывает литеру (0000X..0FFFFX).

PROCEDURE (VAR wr: Writer) WriteByte (x: BYTE)
NEW
Записывает очень короткое целое (-128..127).

PROCEDURE (VAR wr: Writer) WriteSInt (x: SHORTINT)
NEW
Записывает короткое целое (-32768..32767).

PROCEDURE (VAR wr: Writer) WriteXInt (x: INTEGER)
NEW
То же, что и WriteSInt, но имеет параметр типа INTEGER.
Эта процедура предоставлена для упрощения перехода с версии 1.2 на 1.3.

PROCEDURE (VAR wr: Writer) WriteInt (x: INTEGER)
NEW
Записывает целое (-2147483648..2147483647).

PROCEDURE (VAR wr: Writer) WriteLong (x: LONGINT)
NEW
Записывает длинное целое (-9223372036854775808..9223372036854775807).

PROCEDURE (VAR wr: Writer) WriteSReal (x: SHORTREAL)
NEW
Записывает вещественное число (32-битное число в формате IEEE).

PROCEDURE (VAR wr: Writer) WriteXReal (x: REAL)
NEW
То же, что и WriteSReal, но имеет параметр типа REAL.
Эта процедура предоставлена для простого перехода с версии 1.2 на 1.3.

PROCEDURE (VAR wr: Writer) WriteReal (x: REAL)
NEW
Записывает длинное вещественное (64-битное число в формате IEEE).

PROCEDURE (VAR wr: Writer) WriteSet (x: SET)
NEW
Записывает множество (32 элемента).

PROCEDURE (VAR wr: Writer) WriteSString (IN x: ARRAY OF SHORTCHAR)
NEW
Записывает цепочку короктих литер, заканчивающуюся 0X.

PROCEDURE (VAR wr: Writer) WriteXString (IN x: ARRAY OF CHAR)
NEW
То же, что и WriteSString, но параметр  цепочка литер (т.е. не коротких литер прим. Ф.Т.).
Эта процедура предоставлена для упрощения перехода с версии 1.2 на 1.3.

PROCEDURE (VAR wr: Writer) WriteString (IN x: ARRAY OF CHAR)
NEW
Записывает цепочку литер, заканчивающуюся 0X.

PROCEDURE (VAR wr: Writer) WriteStore (x: Store)
NEW
Записывает тип объекта хранения, а затем его содержимое вызовом соответствующей процедуры Externalize. x также может быть NIL, или неопознанным объектом (alien). Перед вызовом Externalize вызывается ExternalizeAs, чтобы позволить объекту хранения указать для записи вместо себя объект-заместитель (proxi). WriteStore записывает x и (через Externalize) все содержащиеся в нём объекты хранения. Циклические ссылки обрабатываются корректно, т.е. объект хранения записывается только однажды, даже если на него в сложном графе несколько ссылок.
Все объекты хранения, записанные одним объектом записи, должны принадлежать одному домену.

предусловие
20	wr.rider # NIL
21	x # NIL  =>  объект записи не должен иметь домена, либо иметь тот же, которому принадлежит x (вместе со всеми ранее записанными объектами записи)

PROCEDURE WriteVersion (version: INTEGER)
NEW
Записывает байт версии.

Предусловие
20	0 <= version <= 127


TYPE AlienComp, AlienPiece, AlienPart, Alien
LIMITED
Эти вспомогательные типы используются внутри Блэкбокса для обработки неопознанных объектов (alien stores).


PROCEDURE Join (s0, s1: Store)
Объединяет два объекта хранения в одном множестве. См. объяснения в начале этого текста.

Предусловие
20	s0 # NIL
21	s1 # NIL
22	s0.Domain() = NIL  OR  s1.Domain() = NIL  OR  s0.Domain() = s1.Domain()

Постусловие
Joined(s0, s1)

PROCEDURE Joined (s0, s1: Store): BOOLEAN
Проверяет, объединены ли два объекта хранения. Joined(x, x) всегда возвращает TRUE, т.е. функция рефлексивна.

Предусловие
20	s0 # NIL
21	s1 # NIL

PROCEDURE Unattached (s: Store): BOOLEAN
Проверяет, не присоединён ли s к домену и не объединён ли он с другими объектами хранения.
Используется редко.

Предусловие
20	s # NIL

PROCEDURE CopyOf (s: Store): Store
Возвращает копию объекта хранения вместе с копией содержимого. 
CopyOf размещает в памяти запись с тем же динамическим типом, что и у s, и инициализирует его вызовом соответствующей процедуры CopyFrom. Выполняется глубокое копирование.

Предусловие
20	s # NIL

Постусловие
(result # NIL) & (result # s)
TYP(result) = TYP(s)

PROCEDURE InitDomain (s: Store)
Инициализирует домен объекта хранения s. См. объяснения в начале этого текста.

Предусловие
20	s # NIL

Постусловие
s.Domain() # NIL

PROCEDURE ExternalizeProxy (s: Store): Store
Заставляет s вызвать свой метод ExternalizeAs, по существу делает следующее:

	IF s # NIL THEN s.ExternalizeAs(s) END;
	RETURN s

PROCEDURE Report (IN msg, p0, p1, p2: ARRAY OF CHAR)
Когда объект хранения сталкивается с какой-либо проблемой во время загрузки, он может сообщить о ней с помощью этой процедуры. Параметры как у процедуры Dialog.ShowParamMsg.