adapter
Adaptador do Modelo de Referência (MR) do openEHR para Seed.
Get more details at codescene.io.
This work is licensed under a Creative Commons Attribution 4.0 International License.
Fábio Nogueira de Lucena - Fábrica de Software - Instituto de Informática (UFG).
Caso de uso
O Modelo de Referência (MR) do openEHR é um conjunto de classes que define os blocos básicos para empacotamento de informações em saúde. Quando essas informações precisam ser transferidas ou mesmo persitidas, existem várias possibilidades.
Como usar (via maven)?
Acrescente a dependência no arquivo pom.xml:
<dependency> <groupId>com.github.kyriosdata.adapter</groupId> <artifactId>oe-seed</artifactId> <version>1.0.0</version> </dependency>
Documentação
Tipos (dos campos)
Os tipos contemplados seguem abaixo, identificados pelo correspondente tipo em Java.
- Os tipos inteiros: BYTE (byte), SHORT (16 bits), INT (int) (32 bits), INT64 (long) (64 bits).
- Os tipos em ponto flutuante: REAL (float) (32 bits), DOUBLE (double) (64 bits).
- O tipo lógico: BOOLEAN (boolean).
- O tipo que representa um caractere: CHAR (char).
- O tipo sequência de caracteres: STRING (String).
- O tipo vetor de bytes: VECTOR (byte[]).
- O tipo intervalo: INTERVAL_INT, INTERVAL_INT64, INTERVAL_REAL, INTERVAL_DOUBLE. Cada um desses tipos é formado por quatro valores. Dois para os limites do intervalo, lower e upper, e outros dois lógicos, lowerIncluded e upperIncluded. Os tipos dos limites do intervalo são definidos pelo tipo do intervalo, por exemplo, INTERVAL_INT faz uso de dois inteiros, enquanto INTERVAL_DOUBLE faz uso de dois valores do tipo ponto-flutuante de precisão dupla.
- O tipo lista: LIST (List). Uma lista é uma coleção de itens, não necessariamente únicos, mas em uma ordem.
- O tipo conjunto: SET (Set). É uma coleção de itens únicos, não podem existir repetições, não há ordem entre eles.
- O tipo dicionário: HASH (Hash). É um dicionário ou mapa, ou seja, uma coleção de valores, cada um deles disponível e associado a uma dada chave.
Tamanho de um registro
Um registro é uma combinação de campos. O tamanho de um registro, portanto, depende da quantidade de bytes necessária para armazenar cada um dos campos do registro. Se um registro é formado exclusivamente por campos de tamanho fixo, então o registro possui tamanho fixo. Caso contrário, o tamanho do registro varia. Ou seja, no pode ser definido antecipadamente e, é único por registro. Por exemplo, o formato de registro definido por uma única STRING que deve registrar um logradouro pode ter o tamanho 30 em um exemplo e 50 em outro. Se inclui uma lista, pode ter 0 elementos em um caso e 20 em outro.
Representação (serialização) de um registro
Um registro é formado por um header seguido dos dados correspondentes aos tipos dos campos do registro, conforme ilustrado abaixo.
+-----------------+
| RECORD |
+-----------------+
| Header | Fields |
+-----------------+
Representação dos dados de um registro
Abaixo segue a ilustração de um registro, sem o detalhamento do header. Esse registro reúne um campo inteiro e duas sequências de caracteres. Ou seja, os campos são dos tipos INT, STRING e STRING. O valor do INT é 23 (faz uso de 4 bytes); a primeira STRING apenas 4 bytes ("nome") e a segunda STRING ocupa outros 7 bytes ("contato"). A primeira linha contendo números abaixo indica os deslocamentos do início de cada campo com base na posição inicial (0). Ou seja, o inteiro faz uso dos bytes de 0 a 3 (inclusive), o "nome" ocupa os quatro bytes seguintes de 4 a 7 (inclusive) e, por último, "contato" faz uso dos bytes de 8 a 14 (inclusive). O décimo quinto byte está além do registro. Nesse arranjo observe que o tipo de tamanho fixo (INT) segue antes dos demais, ou seja, aquele de tamanho fixo segue antes daqueles de tamanho variável.
+------------------------------+
| HEADER | FIELDS |
+------------------------------+
---------|0---|4-----|8--------|15
+------------------------------+
| HEADER | 23 | nome | contato |
+------------------------------+
Representação do header de um registro
O header obrigatoriamente identifica, em seu primeiro byte, o tipo do registro. Observe que não é o formato propriamente dito, mas um identificador que permite localizar o formato empregado pelo registro. Dado que um único byte é empregado, tem-se um limite natural para os possíveis formatos (tipos) de registros.
Em geral o tipo de um registro inclui campos de tamanho variável. Nesses caso, o header deve conter várias informações:
- Tipo, que identifica unicamente o formato do registro.
- Tamanho do registro. Permite rapidamente "saltar" para o próximo registro. Observe que esse valor pode ser "recuperado" a partir do percurso do conteúdo do registro.
- Apontadores. Após campos de tamanho fixo, que não dependem de apontadores, segue o primeiro campo de tamanho variável que também não depende de apontador. Contudo, após o primeiro campo de tamanho variável, todos os demais dependem de "saltar" sobre o conteúdo dos dados para serem localizados ou de apontadores, que não dependem desse percurso. Imagine por exemplo uma STRING. Segundo o tipo de registro ilustrado anteriormente, para o acesso à segunda STRING estão disponíveis duas estratégias: (a) localiza-se o término da STRING anterior (sabe-se que a seguinte é iniciada no byte seguinte) e (b) um apontador no header pode indicar diretamente o início da segunda STRING.
Abaixo é ilustrada a composição do header, conforme comentada acima, onde o tipo de um byte precede o tamanho do registro, 4 bytes, que são seguidos, possivelmente, de apontadores, cada um deles de 4 bytes. Observe que abaixo o valor A identifica o total de apontadores.
+-----------------------------------+
| HEADER |
+-----------------------------------|
|0-----|1--------|5-----------------| 4*A + 5
+-----------------------------------+
| Tipo | Tamanho | Apontadores |
+-----------------------------------+
Abaixo segue ilustração do registro exemplo apresentado anteriormente, agora acrescida do header. Suponha que o tipo de valor 54 identifica unicamente o formato desse registro, ou seja, a sequência formada por um INT, uma STRING e outra STRING.
+-----------------------------------+
| HEADER | FIELDS |
+-----------------------------------+
--------------|0---|4-----|8--------|15
+-----------------------------------+
| 54 | 15 | 8 | 23 | nome | contato |
+-----------------------------------+
Interpretação de cada um dos valores acima:
- Primeiro segue o tipo do registro, valor 54 (suposição estabelecida acima).
- O tipo é seguido do tamanho do registro, 15 bytes de dados. Esse tamanho não é o tamanho total do registro, pois não inclui os bytes empregados pelo header, mas apenas aqueles que dizem respeito aos dados propriamente ditos (ou payload).
- O último valor do header é 8, a posição inicial do campo "contato". Observe que ao manter os campos de tamanho fixo no início, em ordem bem definida, não é necessário indicar a posição deles, nem do primeiro de tamanho variável, nesse caso "nome". Ou seja, para um registro do tipo 54 é suficiente armazenar a posição de início do último campo, posição 8.
Decisões
- Campos de tamanho fixo precedem todos os campos de tamanho variável.
- Valor de posição no header é relativa à posição inicial (0) dos dados, imediatamente após o header.
O registro representado na ilustração acima fornece o comportamento geral. Contudo, há situações especiais que demandam alteração na representação tanto do header quanto do dados. Contudo, isso é melhor compreendido após a introdução de outras questões: (a) blocos e (b) fragmentação de registros.
Blocos (elemento de divisão de um arquivo)
Uma base de dados é armazenada em um arquivo didivido em blocos de tamanho fixo. O tamanho padrão é 4KB. O acesso ao conteúdo da base de dados significa que esses blocos precisam ser transferidos para a memória RAM. No sentido inverso, atualizações precisam ser depositadas no bloco correspondente no arquivo em questão.
Fragmentação de registro
Dado que apenas parte da informação de uma base de dados se encontra em RAM, ou seja, apenas alguns blocos, e que um bloco possui tamanho fixo, enquanto os registros não, é natural que a divisão em blocos "fragmente" um registro no sentido em que parte das informações podem estar no final de um bloco e as demais a partir do bloco seguinte. De fato, um registro pode estar "espalhado" por vários blocos. Em particular, um único campo pode estar espalhado por vários blocos.
Decisão
- Dados de um registro podem estar espalhados por vários blocos consecutivos (contíguos).
Abaixo é ilustrado o cenário onde o registro está disposto em dois blocos, sem perda de generalidade, assuma que são os blocos 6 e 7. Nessa ilustração, a STRING "contato" é dividida em "cont" (bloco 6) e "ato" (bloco 7).
------------ Bloco 6 -----------||----------- Bloco 7 ------------
--------------|0---|4-----|8----||----|15
+-------------------------------------+
| 54 | 15 | 8 | 23 | nome | cont||ato |
+-------------------------------------+
A estratégia acima tem como ponto positivo maximizar o uso de cada byte de um bloco, onde cada byte é utilizado. Por outro lado, deve-se definir como tratar campos "partidos" pela divisão dos dados em blocos. Para o registro de exemplo, apenas o tipo do registro (um único byte), necessariamente está em um único bloco. Para os demais valores, deve-se definir como identificar os "fragmentos" de um campo e montá-lo a partir de tais fragmentos. Noutras palavras, qualquer byte do 1 até o 24 pode ser a diviso de um bloco do seguinte.
+----|1 ----------------------------|24
| 54 | 15 | 8 | 23 | nome | contato |
+-----------------------------------+
Endereços
Um apontador indica o início de um campo relativo ao início dos dados do registro em questão. Ou seja, esse endereço não é o endereço do byte correspondente no arquivo onde é armazenado, também não coincide com o deslocamento referente ao bloco no qual se encontra.