Existem ainda duas palavras reservadas que podem ser utilizadas na especificação do escopo de modificações admissíveis para uma operação:
a) everything: qualquer elemento do espaço de objetos pode ser modificado. Esse é o valor definido como padrão para operações modificadoras, ou seja, se uma operação modificadora não definir nenhuma restrição do tipo
modifiable, assume-se everything.
b) nothing: o espaço de objetos resultante da execução da operação deve ser igual ao espaço de objetos existente no instante da sua invocação. Esse é o valor definido como padrão para as operações de consulta, ou seja, se uma operação de consulta não definir nenhuma restrição do tipo modifiable, assume-se nothing.
7.4 Biblioteca padrão da OCL
Algumas operações definidas na biblioteca padrão da OCL possuem uma definição formal que não é compatível com a sua descrição informal. Em particular, a definição formal das operações select e sum pode gerar resultados incompatíveis com sua descrição informal nos casos em que a coleção contenha algum elemento com valor indefinido.
A definição informal da operação select é equivalente ao operador select da linguagem SQL, ou seja, o seu resultado corresponde ao subconjunto dos elementos para os quais a expressão de seleção resultar no valor verdadeiro, ou seja, os elementos para os quais a expressão resultar em falso, ou em um valor indefinido, não farão parte do subconjunto resultante.
Na especificação da OCL, a semântica da operação select para coleções do tipo
Set é definida em função da operação iterate, conforme ilustrado na Figura 7.22. Para os
source->select(iterator | body) =
source->iterate(iterator; result : Set(T) = Set{} | if body
then result->including(iterator) else result
endif)
Figura 7.22 – Semântica da operação select conforme a especificação da OCL
A operação iterate percorre cada elemento da coleção, atualizando, em cada iteração, o seu resultado com o valor resultante da avaliação da expressão definida após a barra vertical. O valor inicial é definido pela expressão de inicialização associada à variável localizada antes da barra vertical. Portanto, o valor de uma expressão iterate é igual ao resultado obtido na sua última iteração.
O problema nessa definição da operação select ocorre quando a coleção source contém um ou mais elementos com valor indefinido. Como a iteração é baseada em uma expressão if-then-else-endif, se a expressão body resultar em um valor indefinido, o resultado da iteração será indefinido e, portanto, o resultado da operação select será indefinido.
Para tornar a definição da operação select compatível com a sua descrição informal, adotamos a definição descrita na Figura 7.23, segundo a qual, os elementos para os quais a expressão body não resulte no valor verdadeiro são ignorados.
source->select(iterator | body) =
source->iterate(iterator; result : Set(T) = Set{} | if not body.oclIsUndefined() and body then result->including(iterator)
else result endif)
Figura 7.23 – Definição proposta para a operação select
A operação sum é definida para qualquer coleção como a soma dos valores de seus elementos, desde que a operação + esteja definida no tipo do elemento da coleção. Desta forma, a expressão Set (MCCARTHY e HAYES, 1969)->sum() resulta no valor 12. A interpretação aplicada para operações de agregação, na possível presença de elementos com valor indefinido, corresponde à soma dos valores dos elementos que tenham valor conhecido, ou seja, os elementos com valor indefinido são ignorados.
Entretanto, a definição da operação sum na especificação da OCL - post: result = self->iterate(elem; acc : T = 0 | acc + elem) - não equivale a essa interpretação. Como a soma de um valor indefinido com qualquer outro valor resulta num valor indefinido, o resultado da operação sum será sempre indefinido quando a coleção possuir algum elemento indefinido. A Figura 7.24 apresenta a definição semântica que empregamos para a operação sum, onde apenas os elementos de valor definido são computados na soma.
post: result = self->iterate(elem; acc : T = 0 | if elem.oclIsUndefined() then acc
else acc + elem endif)
Figura 7.24 – Definição proposta para a operação sum
Uma outra questão semântica ligada às operações de coleção da OCL está relacionada à definição das operações exists e one. A Figura 7.25 apresenta a definição da operação exists presente na especificação da OCL. A operação source->exists(body) resulta no valor verdadeiro, se a expressão body for verdadeira para pelo menos um elemento da coleção source. Caso a expressão body não seja verdadeira para nenhum elemento da coleção, o resultado será o valor falso, somente se body resultar no valor falso para todos os elementos da coleção. Caso contrário, o valor será indefinido, ou seja, não podemos afirmar se existe ou não algum elemento na coleção para o qual body seja verdadeiro.
source->exists(iterator | body) =
source->iterate(iterator; result : Boolean = false | result or body)
Figura 7.25 – Definição da operação exists
A operação one é semelhante à operação exists, porém ela resulta no valor verdadeiro somente se a expressão body for verdadeira para um e apenas um elemento da coleção. Entretanto, como a operação one é definida em função da operação select, o seu resultado nunca será indefinido.
source->one(iterator | body) =
source->select(iterator | body)->size() = 1
A Figura 7.26 apresenta a definição que adotamos para a operação one. De acordo com essa definição, se a coleção tiver algum elemento de valor indefinido, o resultado da operação será indefinido. Dessa forma, a operação one somente resulta no valor verdadeiro, se a expressão body for verdadeira para apenas um elemento da coleção e falsa para todos os demais elementos da coleção.
source->one(iterator | body) = let nTrue : Boolean =
source->iterate(iterator; result : Integer = 0 | if body
then result + 1 else result endif) in nTrue = 1
Figura 7.27 – Definição proposta para a operação one
Além das modificações na semântica de algumas operações definidas na biblioteca padrão da OCL, acrescentamos novos tipos primitivos e novas operações a alguns tipos definidos na biblioteca padrão da OCL, que são particularmente importantes para a elaboração de modelos de sistema de informação transacionais (CORREA e WERNER, 2004c).
Os tipos acrescentados à biblioteca são Date, para a representação e manipulação de datas, e DateTime, para a representação e manipulação de instantes de tempo. Várias operações foram adicionadas à classe String, uma vez que a especificação da OCL define apenas operações de substring e concatenação. A elas foram adicionadas operações de busca de substring, conversão para maiúsculas e minúsculas, comparação por expressões regulares, entre outras. Além disso, foram definidas novas operações de agregação para as coleções: avg (média dos elementos), min (elemento de menor valor) e max (elemento de maior valor). O apêndice A apresenta a definição das classes e operações adicionadas à biblioteca padrão.