Difference between revisions of "Programação com Objectos/Projecto de Programação com Objectos/Enunciado do Projecto de 2017-2018 (rascunho de Agosto)"

From Wiki**3

< Programação com Objectos‎ | Projecto de Programação com Objectos
(Salvaguarda do Estado Actual do Interpretador)
(Exemplo de ficheiro a importar)
 
(213 intermediate revisions by the same user not shown)
Line 4: Line 4:
 
<!--'''<font color="red">ÉPOCA NORMAL</font>'''-->
 
<!--'''<font color="red">ÉPOCA NORMAL</font>'''-->
  
O objectivo do projecto é criar uma aplicação que gere os itinerários de um passageiro que usa comboios.
+
O objectivo do projecto é criar uma aplicação que gere os itinerários de passageiros que usam comboios.
  
Os itinerários são constituídos por um ou mais segmentos, realizados por comboios que cumprem horários pré-definidos.
+
Os itinerários são constituídos por um ou mais segmentos, realizados por comboios que cumprem serviços pré-definidos.
  
 
Neste texto, o tipo '''negrito''' indica um literal (i.e., é exactamente como apresentado); o símbolo <amsmath>\verb*| |</amsmath> indica um espaço; e o tipo ''itálico'' indica uma parte variável (i.e., uma descrição).
 
Neste texto, o tipo '''negrito''' indica um literal (i.e., é exactamente como apresentado); o símbolo <amsmath>\verb*| |</amsmath> indica um espaço; e o tipo ''itálico'' indica uma parte variável (i.e., uma descrição).
  
= Estrutura dos horários e dos itinerários =
+
= Estrutura dos serviços, dos passageiros e dos itinerários =
  
Existem dois conceitos básicos: horário e itinerário.
+
Existem três conceitos básicos: serviços, passageiros e itinerários.
  
== Estrutura de um horário ==
+
== Estrutura de um serviço ==
  
Um horário é um percurso realizado por um comboio ao longo de vários locais. A cada local está associado o momento de partida do comboio. <!--Por simplicidade, considera-se que a hora de partida é sempre igual à de chegada.-->
+
Um serviço é um percurso realizado por um comboio ao longo de várias estações. A cada estação de um serviço está associado o momento de partida do comboio. Assume-se que todos os serviços se realizam em todos os dias. Além das informações anteriores, o serviço tem ainda um identificador numérico único (definido na criação do serviço).
  
<!--{{CollapsedCode|Exemplo de horário com duas classes de serviço: Serviço 131: Lisboa >> Braga|
+
{{CollapsedCode|Exemplo de serviço: Serviço 850: Valença >> Porto - Campanhã|
  Serviço #131 - Conforto 46.50 / Turística 32.80
+
   Serviço #850 - 10.95
  07:00 Lisboa - Santa Apolónia
 
  07:09 Lisboa - Oriente
 
  08:45 Coimbra-B
 
  09:12 Aveiro
 
  09:39 Vila Nova de Gaia
 
  09:46 Porto - Campanhã
 
  10:09 Famalicão
 
  10:15 Nine
 
  10:25 Braga
 
}}
 
-->
 
{{CollapsedCode|Exemplo de horário com duas classes de serviço: Serviço 180: Porto >> Faro|
 
  Serviço #180 - Conforto 70.70 / Turística 51.50
 
  05:47 Porto - Campanhã
 
  05:52 Vila Nova de Gaia
 
  06:21 Aveiro
 
  06:46 Coimbra-B
 
  08:23 Lisboa - Oriente
 
  08:31 Lisboa - Entrecampos
 
  09:06 Pinhal Novo
 
  10:54 Tunes
 
  11:01 Albufeira - Ferreiras
 
  11:13 Loulé
 
  11:23 Faro
 
}}
 
 
 
{{CollapsedCode|Exemplo de horário com uma classe de serviço: Serviço 850: Valença >> Porto|
 
   Serviço #850 - Única 10.95
 
 
   07:36 Valença
 
   07:36 Valença
 
   07:47 Vila Nova de Cerveira
 
   07:47 Vila Nova de Cerveira
Line 64: Line 36:
 
}}
 
}}
  
Cada segmento de um horário tem um custo base, que depende da distância entre os locais e do tipo de serviço. Por simplicidade, e sem limitação de funcionalidade, considera-se que o custo é uniforme entre estações, ou seja, se um horário tiver um custo total  '''C''', para uma dada classe de serviço, e tiver '''N''' estações, cada segmento terá um curso '''S = C / (N-1)'''.  
+
Cada segmento de um serviço tem um custo base, que depende da distância entre estações.  A distância entre estações corresponde ao tempo gasto na viagem entre essas estações. Assim, considerando que a totalidade do custo do serviço 850 (no exemplo acima) é de €10.95 (para 129 minutos), o custo do segmento Viana do Castelo >> Nine (para 49 minutos) é de 10.95 * 49 / 129 = €4.16.
  
O preço pago pelo passageiro depende ainda do tipo de cliente, que pode ser normal ou de outros tipos (a adição de novos tipos deve ter impacto reduzido no código da aplicação).
+
O preço pago pelo passageiro depende ainda da sua categoria, que pode ser normal ou outra (a adição de novas categorias deve ter impacto reduzido no código da aplicação).
  
Cada comboio suporta um ou mais serviços. As classes de serviço são "conforto", "turística" e "única".
+
Considera-se que apenas existem as estações que aparecem em, pelo menos, um serviço.
 +
 
 +
== Estrutura de um passageiro ==
 +
 
 +
Um passageiro tem um identificador único e um nome (cadeia de caracteres). O identificador único é  atribuído automaticamente e de forma sequencial no momento do registo do passageiro, sendo atribuído o identificador 0 (zero) ao primeiro passageiro.
 +
 
 +
Os passageiros são categorizados como normais, frequentes ou especiais. Um passageiro é considerado frequente enquanto o valor dos últimos 10 itinerários for superior a €250. Volta a ser normal se esta condição deixar de se verificar. Um cliente é considerado especial enquanto tiver gasto mais de €2500 nos últimos 10 itinerários. Deixa de ser especial se esta condição deixar de se verificar. Se se verificarem simultâneamente as condições para ser um cliente frequente e especial, o cliente é considerado especial. Aplicam-se as seguintes condições para atribuição de descontos em itinerários: passageiros normais, 0%; passageiros frequentes, 15%; passageiros especiais, 50%.
  
 
== Estrutura de um itinerário ==
 
== Estrutura de um itinerário ==
  
Um itinerário é um conjunto de segmentos, correspondentes a partes de horários. Cada segmento tem um local e hora de partida e um local e uma hora de chegada. É possível saber, através do horário associado, qual o percurso a realizar nesse segmento. O custo de cada segmento é o do percurso realizado no horário e serviço correspondente.
+
Um itinerário corresponde a uma viagem realizada num dado dia, com base num conjunto de segmentos lógicos, correspondentes a partes de serviços, i.e., partidas (ou chegadas) agendadas para cada estação. <!-- Cada segmento tem uma estação e hora de partida e uma estação e hora de chegada.-->É possível saber, através do serviço associado, qual o percurso a realizar nesse segmento.  
  
O custo do itinerário é a soma dos custos dos segmentos individuais. Dependendo do tipo de cliente, podem ser realizados descontos em alguns tipos de serviço. A classe de serviço está associada a cada segmento e não ao itinerário em si.  
+
O custo base do itinerário é a soma dos custos parciais de cada serviço que o constitui, correspondentes aos segmentos realizados nesses serviços. Dependendo da categoria do passageiro, podem ser realizados descontos.
  
= Funcionalidade da Aplicação =
+
Os itinerários são identificados por um número de ordem no contexto de cada passageiro. Não é possível remover itinerários já registados.
  
A aplicação permite manter informação sobre horários e criar novos itinerários para o passageiro. Possui também várias formas de preservar o seu estado (não é possível manter várias versões do estado do interpretador em simultâneo).
+
{{CollapsedCode|Exemplo de itinerário: Évora >> Loulé|
 +
<text>
 +
Itinerário 3 para 2017-10-18 @ 25.84
 +
Serviço #690 @ 6.37
 +
07:06 Évora
 +
07:17 Casa Branca
 +
07:31 Vendas Novas
 +
07:53 Pinhal Novo
 +
Serviço #180 @ 19.47
 +
09:06 Pinhal Novo
 +
10:54 Tunes
 +
11:01 Albufeira - Ferreiras
 +
11:13 Loulé
 +
</text>
 +
}}
  
== Interpretação de expressões ==
+
= Funcionalidade da aplicação =
  
As expressões, apresentadas ao interpretador em forma de texto, devem ser interpretadas antes de serem armazenadas. O texto original não é preservado. Note-se que o processo de interpretação (conversão do texto para estruturas/objectos) é diferente do de avaliação (conversão das estruturas/objectos para valores).
+
A aplicação permite manter informação sobre serviços. Permite ainda registar e gerir passageiros e criar novos itinerários para eles. Possui ainda a capacidade de preservar o seu estado (não é possível manter várias versões do estado da aplicação em simultâneo).
  
O texto a interpretar (expressões ou programas) pode ser providenciado via interface do próprio interpretador ou na forma de um ficheiro que contém um programa.
+
A base de dados de serviços é [[#Leitura de Dados a Partir de Ficheiros Textuais|carregada no início da aplicação]]. Não é possível adicionar ou remover serviços durante a execução da aplicação.
  
Uma falha de interpretação causa o lançamento de uma excepção no analisador do interpretador.
+
{{Suggestion|Note-se que não é necessário implementar de raiz a aplicação: já existem classes que representam e definem a interface geral da  funcionalidade do ''core'' da aplicação, tal como é visível pelos comandos da aplicação.}}
 +
{{CVSCode|A interface geral do ''core''  já está parcialmente implementada na classe '''mmt.TicketOffice''' e outras fornecdidas (cujos nomes devem ser mantidos), devendo ser adaptadas onde necessário. É ainda necessário criar e implementar as restantes classes que suportam a operação da aplicação.}}
  
{{Suggestion|Note-se que não é necessário implementar de raiz o analisador de expressões e programas do interpretador. Um analisador já parcialmente concretizado, '''mmt.Parser''', está disponível. Este analisador é responsável por processar um programa ou uma expressão e realizar a conversão de texto para a estrutura de dados correspondente. Será apenas necessário indicar as entidades utilizadas para representar um programa ou uma expressão.}}
+
== Consultas de Serviços ==
  
== Armazenamento de programas ==
+
É possível fazer várias consultas sobre serviços (ver também abaixo). É possível ver todos os serviços, inspeccionar um serviço particular e inspeccionar serviços com base na especificação de estações. Podem ser definidas, no futuro, outras formas de pesquisa e de apresentação de serviços.
  
O interpretador permite associar nomes a programas, preservando-os. Note-se que isto não corresponde a guardar valores calculados por esses programas, mas a guardar as suas descrições, i.e., as resultantes da interpretação da forma textual correspondente. Se um nome já estiver em uso, a associação ao programa anterior é perdida.
+
== Manipulação de Passageiros ==
  
== Interpretação de programas ==
+
A aplicação permite realizar várias operações sobre passageiros. É possível obter a lista completa de passageiros conhecidos, assim como informação detalhada sobre passageiros individuais. É ainda possível registar novos passageiros e alterar o nome dos passageiros.
  
A interpretação de programas e, consequentemente, das suas expressões, deve ser feita de forma flexível. Ou seja, deve ser possível -- sem alterar o código das expressões ou do interpretador -- definir novas formas de avaliação. Por omissão, a avaliação corresponde simplesmente à apresentação de uma forma textual do programa (e das suas expressões).
+
== Manipulação de Itinerários ==
  
Uma outra avaliação possível é o cálculo dos valores das expressões do programa, correspondente à execução desse programa.
+
A aplicação permite obter informações sobre todos os itinerários já comprados ou, em pormenor, sobre os itinerários de passageiros específicos. Permite ainda criar novos itinerários.
 +
 
 +
Os passageiros são creditados com o valor monetário de cada itinerário comprado e com o tempo decorrido nesses itinerários (medido entre os pontos extremos). <!--O crédito destes valores depende da categoria do passageiro.-->
  
 
== Serialização ==
 
== Serialização ==
  
É possível reiniciar, guardar e recuperar o estado actual da aplicação, preservando todos a informação de horários e itinerários.
+
É possível reiniciar (ou seja, mantendo a informação sobre serviços, eliminar a informação sobre passageiros e itinerários), guardar e recuperar o estado actual da aplicação, preservando todos a informação sobre serviços, passageiros  e itinerários.
  
= Interacção com o Utilizador =
+
= Interacção com o utilizador =
  
 
Descreve-se nesta secção a '''funcionalidade máxima''' da interface com o utilizador. Em geral, os comandos pedem toda a informação antes de proceder à sua validação (excepto onde indicado). Todos os menus têm automaticamente a opção '''Sair''' (fecha o menu).
 
Descreve-se nesta secção a '''funcionalidade máxima''' da interface com o utilizador. Em geral, os comandos pedem toda a informação antes de proceder à sua validação (excepto onde indicado). Todos os menus têm automaticamente a opção '''Sair''' (fecha o menu).
Line 112: Line 107:
 
As excepções usadas na interacção, excepto se indicado, são subclasses de '''pt.tecnico.po.ui.DialogException''', são lançadas pelos comandos e tratadas por '''pt.tecnico.po.ui.Menu'''. Outras excepções não devem substituir as fornecidas nos casos descritos.
 
As excepções usadas na interacção, excepto se indicado, são subclasses de '''pt.tecnico.po.ui.DialogException''', são lançadas pelos comandos e tratadas por '''pt.tecnico.po.ui.Menu'''. Outras excepções não devem substituir as fornecidas nos casos descritos.
  
{{CVSCode|Note-se que os comandos e menus a seguir descritos, assim como o programa principal, já estão implementados nas classes das ''packages'' '''mmt.app''', '''mmt.app.main''' e '''mmt.app.evaluator'''. Estas classes são de uso obrigatório e estão disponíveis no [[Programação com Objectos/Projecto de Programação com Objectos/Repositório CVS|CVS]] (módulo '''mmt-app''').}}
+
{{CVSCode|Note-se que o programa principal e os comandos e menus, a seguir descritos, já estão parcialmente implementados nas ''packages'' '''mmt.app''', '''mmt.app.main''', '''mmt.app.service''', '''mmt.app.passenger''' e '''mmt.app.itinerary'''. Estas classes são de uso obrigatório e estão disponíveis no [[Programação com Objectos/Projecto de Programação com Objectos/Repositório CVS|CVS]] (módulo '''mmt-app''').}}
  
 
== Menu Principal ==
 
== Menu Principal ==
  
As acções do menu permitem gerir a salvaguarda do estado da aplicação: [[#Salvaguarda do Estado Actual do Interpretador|Criar]], [[#Salvaguarda do Estado Actual do Interpretador|Abrir]], [[#Salvaguarda do Estado Actual do Interpretador|Guardar]], [[#Criação, Leitura e Escrita de Programas|Criar Programa]], [[#Criação, Leitura e Escrita de Programas|Ler Programa]], [[#Criação, Leitura e Escrita de Programas|Escrever Programa]] e [[#Manipulação de Programa|Manipulação de Programa]]. A classe '''mmt.app.main.Message''' define os métodos para geração das mensagens de diálogo. Inicialmente, o interpretador está vazio.
+
As acções do menu permitem gerir a salvaguarda do estado da aplicação e abrir submenus. A lista completa é a seguinte: [[#Salvaguarda do estado actual da aplicação|Reiniciar]], [[#Salvaguarda do estado actual da aplicação|Abrir]], [[#Salvaguarda do estado actual da aplicação|Guardar]] e [[#Gestão e consulta de dados da aplicação|Consulta de Serviços]], [[#Gestão e consulta de dados da aplicação|Manipulação de Passageiros]] e [[#Gestão e consulta de dados da aplicação|Manipulação de Itinerários]]. Inicialmente, a aplicação apenas tem informação sobre os serviços que foram carregados no arranque.
 +
 
 +
As etiquetas das opções deste menu estão definidas na classe '''mmt.app.main.Label'''. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe '''mmt.app.main.Message'''.
 +
 
 +
{{CVSCode|Estes comandos já estão implementados  nas classes da ''package'' '''mmt.app.main''' (disponível no CVS), respectivamente: '''DoReset''', '''DoOpen''', '''DoSave''', '''DoOpenServicesMenu''', '''DoOpenPassengersMenu''', '''DoOpenItinerariesMenu'''.}}
  
=== Salvaguarda do Estado Actual da Aplicação ===
+
=== Salvaguarda do estado actual da aplicação ===
  
O conteúdo da aplicação (inclui todos os horários, clientes e itinerários actualmente carregados pela aplicação) pode ser guardado para posterior recuperação (via serialização Java: '''java.io.Serializable'''). Na leitura e escrita do estado da aplicação, devem ser tratadas as excepções associadas. A funcionalidade é a seguinte:
+
O conteúdo da aplicação (inclui todos os serviços, passageiros e itinerários actualmente em memória) pode ser guardado para posterior recuperação (via serialização Java: '''java.io.Serializable'''). Na leitura e escrita do estado da aplicação, devem ser tratadas as excepções associadas. A funcionalidade é a seguinte:
  
* '''Reiniciar''' -- Reinicia a aplicação: destrói toda a informação sobre clientes e itinerários; mantém a informação sobre horários.
+
* '''Reiniciar''' -- Reinicia a aplicação: destrói toda a informação sobre passageiros e itinerários; mantém toda a informação sobre serviços.
* '''Abrir''' -- Carrega os dados de uma sessão anterior a partir de um ficheiro (ficando associado à aplicação). A informação a carregar compreende horários, clientes e itinerários. Pede-se o nome do ficheiro a abrir ('''openFile()'''). Caso o ficheiro não exista, é apresentada a mensagem '''fileNotFound()'''.
+
* '''Abrir''' -- Carrega os dados de uma sessão anterior a partir de um ficheiro (ficando associado à aplicação). A informação a carregar compreende serviços, passageiros e itinerários. Pede-se o nome do ficheiro a abrir ('''openFile()'''). Caso o ficheiro não exista, é apresentada a mensagem '''fileNotFound()'''.
* '''Guardar''' -- Guarda o estado actual da aplicação (horários, clientes e itinerários) no ficheiro associado. Se não existir associação, pede-se o nome do ficheiro a utilizar, ficando a ele associado. Esta interacção realiza-se através do método '''newSaveAs()'''. Não é executada nenhuma acção se não existirem alterações desde a última salvaguarda.
+
* '''Guardar''' -- Guarda o estado actual da aplicação (serviços, passageiros e itinerários) no ficheiro associado. Se não existir associação, pede-se o nome do ficheiro a utilizar, ficando a ele associado. Esta interacção realiza-se através do método '''newSaveAs()'''. Não é executada nenhuma acção se não existirem alterações desde a última salvaguarda.
  
 
As opções '''Reiniciar''' e '''Abrir''' substituem a informação na aplicação nas condições indicadas acima.
 
As opções '''Reiniciar''' e '''Abrir''' substituem a informação na aplicação nas condições indicadas acima.
Line 130: Line 129:
 
A opção '''Sair''' nunca guarda o estado da aplicação, mesmo que existam alterações.
 
A opção '''Sair''' nunca guarda o estado da aplicação, mesmo que existam alterações.
  
{{CVSCode|Estes comandos já estão parcialmente implementados  nas classes da ''package'' '''mmt.app.main''' (disponível no CVS), respectivamente: '''New''', '''Open''', '''Save'''.}}
+
=== Gestão e consulta de dados da aplicação ===
 +
 
 +
* '''Consulta de Serviços''' -- Abre o menu de consulta de serviços (se existirem serviços registados).
 +
* '''Gestão de Passageiros''' -- Abre o menu de gestão de passageiros (se existirem passageiros registados).
 +
* '''Gestão de Itinerários''' -- Abre o menu de gestão de itinerários (se existirem itinerários registados).
 +
 
 +
== Menu de Consulta de Serviços ==
 +
 
 +
Este menu permite efectuar consultas sobre a base de dados de serviços. A lista completa é a seguinte: [[#Mostrar todos os serviços|Mostrar todos os serviços]], [[#Mostrar serviço com um dado número|Mostrar serviço com um dado número]], [[#Mostrar serviços com origem numa estação dada|Mostrar serviços com origem numa estação dada]], [[#Mostrar serviços com término numa estação dada|Mostrar serviços com término numa estação dada]].
 +
 
 +
As etiquetas das opções deste menu estão definidas na classe '''mmt.app.service.Label'''. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe '''mmt.app.service.Message'''.
 +
 
 +
Sempre que for pedido o identificador de um serviço ('''requestServiceId()''') e o serviço não existir, é lançada a excepção '''mmt.app.NoSuchServiceException'''.
 +
Sempre que for pedido o nome de uma estção ('''requestStationName()''') e a estação não existir, é lançada a excepção '''mmt.app.NoSuchStationException'''.
 +
 
 +
{{CVSCode|Estes comandos já estão implementados  nas classes da ''package'' '''mmt.app.service''' (disponível no CVS), respectivamente: '''DoShowAllServices''', '''DoShowServiceByNumber''', '''DoShowServicesDepartingFromStation''', '''DoShowServicesArrivingAtStation'''.}}
 +
 
 +
=== Mostrar todos os serviços ===
 +
 
 +
Apresenta informações sobre todos os serviços conhecidos. A lista é ordenada pelo identificador do serviço e o formato é o descrito em [[#Mostrar serviço com um dado número|Mostrar serviço com um dado número]].
 +
 
 +
=== Mostrar serviço com um dado número ===
 +
 
 +
Apresenta informações sobre um serviço. Depois de pedir o identificador do serviço ('''requestServiceId()'''), apresenta a informação sobre o serviço, tal como indicado no formato seguinte, para um serviço com N paragens (HH;MM representa o tempo em horas e minutos). O preço deve ser apresentado com duas casas decimais.
 +
 
 +
Serviço #<font color="brown">''número-do-serviço''</font> @ <font color="brown">''preço''</font>
 +
<font color="brown">''HH:MM''</font> <font color="brown">''nome-de-estação-1''</font>
 +
...
 +
<font color="brown">''HH:MM''</font> <font color="brown">''nome-de-estação-N''</font>
 +
 
 +
{{CollapsedCode|Exemplo de apresentação de serviço: Serviço 180: Porto >> Faro|
 +
<text>
 +
Serviço #180 @ 51.50
 +
05:47 Porto - Campanhã
 +
05:52 Vila Nova de Gaia-Devesas
 +
06:21 Aveiro
 +
06:46 Coimbra-B
 +
08:23 Lisboa - Oriente
 +
08:31 Lisboa - Entrecampos
 +
09:06 Pinhal Novo
 +
10:54 Tunes
 +
11:01 Albufeira - Ferreiras
 +
11:13 Loulé
 +
11:23 Faro
 +
</text>
 +
}}
 +
 
 +
=== Mostrar serviços com origem numa estação dada ===
 +
 
 +
Apresenta os números e estações terminais dos serviços com origem na estação indicada como resposta a '''requestStationName()''', ordenado por hora de partida do serviço.
 +
 
 +
O formato de apresentação é o descrito em [[#Mostrar serviço com um dado número|Mostrar serviço com um dado número]].
 +
 
 +
=== Mostrar serviços com término numa estação dada ===
 +
 
 +
Apresenta os números e estações de origem dos serviços com término na estação indicada como resposta a '''requestStationName()''', ordenado por hora de chegada do serviço.
  
=== Criação, Leitura e Escrita de Programas ===
+
O formato de apresentação é o descrito em [[#Mostrar serviço com um dado número|Mostrar serviço com um dado número]].
  
É possível criar novos programas (vazios), ler programas a partir de ficheiros textuais e escrever programas sob a forma de ficheiros textuais. As operações são as seguintes:
+
== Menu de Gestão de Passageiros ==
  
* '''Criar Programa''' -- Permite criar um programa vazio. É pedido o nome do programa através de '''requestProgramId()'''. A utilização de um nome previamente registado substitui o programa a ele associado por um programa vazio.
+
Este menu permite efectuar operações sobre um passageiro. A lista completa é a seguinte: [[#Mostrar passageiros|Mostrar passageiros]], [[#Mostrar passageiro|Mostrar passageiro]], [[#Registar passageiro|Registar passageiro]], [[#Alterar nome de passageiro|Alterar nome de passageiro]].
* '''Ler Programa''' -- Permite ler e interpretar o texto de um programa a partir de um ficheiro. É lido o ficheiro indicado pelo método '''programFileName()'''. A leitura de novo ficheiro com o mesmo nome substitui o programa anterior com esse nome (nome do ficheiro indicado). Note-se que o novo programa é interpretado: se a excepção '''mmt.ParserException''' (do "core") for recebida, então a excepção '''mmt.app.BadFileException''' (a desenvolver) deve ser lançada pelo comando. Neste caso, o programa não deve ser registado.
 
* '''Escrever Programa''' -- Permite guardar um programa como um ficheiro de texto, passível de ser lido novamente pelo interpretador. O interpretador pede o identificador do programa '''requestProgramId()''' e o nome do ficheiro onde deve ser guardado o programa, através do método '''programFileName()'''. Se o programa não existir, é comunicado o erro através de '''noSuchProgram()'''. Escritas no mesmo ficheiro substituem o conteúdo anterior, não ficando associado ao interpretador.
 
  
{{CVSCode|Estes comandos já estão parcialmente implementados  nas classes da ''package'' '''mmt.app.main''' (disponível no CVS), respectivamente: '''NewProgram''', '''ReadProgram''', '''WriteProgram'''.}}
+
As etiquetas das opções deste menu estão definidas na classe '''mmt.app.passenger.Label'''. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe '''mmt.app.passenger.Message'''.
{{Aviso|É necessário definir a excepção '''mmt.app.BadFileException''', devendo o seu construtor receber como argumento o nome do ficheiro que contém o programa a ler.}}
 
  
=== Consulta de Horários ===
+
Sempre que for pedido o identificador de um passageiro ('''requestPassengerId()''') e o passageiro não existir, é lançada a excepção '''mmt.app.NoSuchPassengerException'''.
  
Esta opção permite a consulta de horários associados ao serviços de transporte. É pedido o identificador do serviço a consultar: '''requestScheduleId()'''. Se o serviço não existir, é comunicado o erro através de '''noSuchSchedule()'''.
+
{{CVSCode|Estes comandos já estão implementados  nas classes da ''package'' '''mmt.app.passenger''' (disponível no CVS), respectivamente: '''DoShowAllPassengers''', '''DoShowPassengerById''', '''DoRegisterPassenger''', '''DoChangePassengerName'''.}}
  
{{CVSCode|Este comando já está implementado  na classe '''mmt.app.main.ShowService''' (disponível no CVS).}}
+
=== Mostrar passageiros ===
  
=== Manipulação de Itinerários ===
+
Apresenta informações sobre todos os passageiros conhecidos. A lista é ordenada pelo identificador e o formato é o descrito em [[#Mostrar passageiro|Mostrar passageiro]].
  
Abre o menu de edição de um itinerário. É pedido o identificador do itinerário '''requestIteneraryId()'''. Se o itinerário não existir, é comunicado o erro através de '''noSuchItinerary()'''.
+
=== Mostrar passageiro ===
  
{{CVSCode|Este comando já está implementado  na classe '''mmt.app.main.ManageIninerary''' (disponível no CVS).}}
+
Apresenta informações sobre um passageiro. Depois de pedir o identificador do passageiro ('''requestPassengerId()'''), apresenta o nome, a categoria do passageiro ('''NORMAL''', '''FREQUENTE''', '''ESPECIAL'''), o número de itinerários adquiridos, o tempo acumulado (formato HH:MM) correspondente aos itinerários adquiridos e o valor pago por esses itinerários (o valor pago deve ser apresentado com duas casas decimais).
  
== Menu de Manipulação de Programas ==
+
Formato:
 +
''id'' | ''nome'' | ''categoria'' | ''número-de-itinerários'' | ''valor-pago'' | ''tempo-acumulado''
  
Este menu permite efectuar operações sobre um programa. A lista completa é a seguinte: [[#Listar programa|Listar programa]], [[#Executar|Executar]], [[#Adicionar expressão|Adicionar expressão]], [[#Substituir expressão|Substituir expressão]], [[#Mostrar os identificadores presentes no programa|Mostrar os identificadores presentes no programa]], [[#Mostrar os identificadores não inicializados do programa|Mostrar os identificadores não inicializados do programa]].
+
Exemplo:
 +
1234|John Doe|NORMAL|123|9876.54|123:45
  
As etiquetas das opções deste menu estão definidas na classe '''mmt.app.evaluator.Label'''. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe '''mmt.app.evaluator.Message'''.
+
=== Registar passageiro ===
  
{{CVSCode|Estes comandos já estão parcialmente implementados  nas classes da ''package'' '''mmt.app.evaluator''' (disponível no CVS), respectivamente: '''ShowProgram''', '''RunProgram''', '''AddExpression''', '''ReplaceExpression''', '''ShowAllIdentifiers''', '''ShowUninitializedIdentifiers'''.}}
+
Permite registar um passageiro no sistema. É pedido o nome do passageiro ('''requestPassengerName()'''). O novo passageiro é categorizado como '''NORMAL'''.
  
=== Listar programa ===
+
=== Alterar nome de passageiro ===
  
Este comando apresenta a lista de expressões do programa em formato textual.
+
Permite alterar o nome do passageiro. Para tal, pede-se o identificador do passageiro cujo nome deve ser alterado ('''requestPassengerId()''') e o novo nome ('''requestPassengerName()''').
  
=== Adicionar expressão ao programa ===
+
== Menu de Gestão de Itinerários ==
  
Este comando permite adicionar uma nova expressão ao programa. Para tal, é pedido o índice correspondente à posição de inserção (o primeiro índice é 0), através de '''requestPosition()''', sendo a nova expressão aí inserida. A nova expressão é lida como resposta a '''requestExpression()'''.  
+
Este menu permite efectuar operações sobre itinerários. A lista completa é a seguinte: [[#Mostrar todos os itinerários|Mostrar todos os itinerários]], [[#Mostrar itinerários associados a um passageiro|Mostrar itinerários associados a um passageiro]], [[#Registar itinerário para um passageiro|Registar itinerário para um passageiro]]<!--, [[#Mostrar histogramas por serviço|Mostrar histogramas por serviço]]-->.
  
Se a indicação de posição for igual ao número de elementos do programa, a nova expressão é inserida no final do programa (após todas as outras).
+
As etiquetas das opções deste menu estão definidas na classe '''mmt.app.itinerary.Label'''. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe '''mmt.app.itinerary.Message'''.
  
Se a indicação de posição for inválida, o comando deve lançar a excepção '''mmt.app.BadPositionException''' e o programa não é alterado.
+
{{CVSCode|Estes comandos já estão parcialmente implementados  nas classes da ''package'' '''mmt.app.passenger''' (disponível no CVS), respectivamente: '''DoShowAllItineraries''', '''DoShowPassengerItineraries''', '''DoRegisterItinerary''', '''DoShowTotalValue''', '''DoShowServiceHistograms'''.}}
  
Note-se que a nova expressão é interpretada: se a excepção '''mmt.ParserException''' (do "core") for recebida, então a excepção '''mmt.app.BadExpressionException''' deve ser lançada pelo comando. Neste caso, o programa não deve ser alterado.
+
=== Mostrar todos os itinerários ===
  
=== Substituir expressão no programa ===
+
Apresenta todos os itinerários registados para todos os passageiros.  O formato de apresentação é como para os serviços, mas apenas se apresenta o segmento percorrido e o respectivo custo. A informação é apresentada para cada passageiro, sendo os passageiros apresentados por ordem crescente do seu número de identificação. <!-- e, para cada passageiro, pela estação de origem do itinerário.-->
  
Este comando permite substituir uma expressão existente no programa por uma nova expressão. Para tal, é pedido o índice correspondente à posição de inserção (o primeiro índice é 0), através de '''requestPosition()''', sendo a expressão aí existente substituída pela nova expressão. A nova expressão é lida como resposta a '''requestExpression()'''.  
+
O formato de apresentação é como definido para a [[#Mostrar itinerários associados a um passageiro|apresentação dos itinerários de um passageiro]], mas apresenta-se a informação relativamente a todos os passageiros.
  
Se a indicação de posição for inválida, o comando deve lançar a excepção '''mmt.app.BadPositionException'''  e o programa não é alterado.
+
Note-se que a apresentação de itinerários apenas tem significado quando existem itinerários para mostrar, i.e., não se devem apresentar passageiros que não têm itinerários.
  
Note-se que a nova expressão é interpretada: se a excepção '''mmt.ParserException''' (do "core") for recebida, então a excepção '''mmt.app.BadExpressionException''' deve ser lançada pelo comando. Neste caso, o programa não deve ser alterado.
+
=== Mostrar itinerários associados a um passageiro ===
  
=== Mostrar os identificadores presentes no programa ===
+
Apresenta os itinerários do passageiro cujo identificador é pedido através de '''requestPassengerId()'''. A lista é ordenada pela data dos itinerários.
  
Este comando permite listar todos os identificadores presentes no programa (apresentando um identificador por linha, por ordem alfabética), tanto nas expressões de definição, como nas que usam identificadores.
+
Se o passageiro não tiver itinerários, deve ser apresentada a mensagem '''noItineraries()'''.
  
=== Mostrar os identificadores do programa sem inicialização explícita ===
+
O formato de apresentação é análogo ao dos serviços, iniciado com uma linha com o identificador do passageiro, à qual se segue a lista de itinerários, cada um precedido da linha com o número de ordem do itinerário (no contexto de cada passageiro) e da data correspondente:
  
Este comando permite listar todos os identificadores presentes no programa que não são objecto de nenhuma incialização explícita (apresentando um identificador por linha, por ordem alfabética), i.e., não  terem um valor previamente associado via operador '''set'''.
+
  <nowiki>==</nowiki> Passageiro <font color="brown">''identificador-do-passageiro''</font>: <font color="brown">''nome-do-passageiro''</font> <nowiki>==</nowiki>
 +
  Itinerário <font color="brown">''número-de-ordem-do-itinerário''</font> para <font color="brown">''data''</font> @ <font color="brown">''preço-total-do-itinerário''</font>
  
= Leitura de Programas a Partir de Ficheiros Textuais =
+
O preço associado a cada serviço no itinerário é o da fracção do tempo do serviço nesse itinerário, com duas casas decimais. O preço do itinerário é a soma (sem arredondamentos) dos preços dos segmentos que o formam, apresentado com duas casas decimais.
 +
 
 +
{{CollapsedCode|Exemplo de apresentação de itinerários para um passageiro|
 +
<text>
 +
== Passageiro 2: José Simão ==
 +
 
 +
Itinerário 1 para 2017-10-18 @ 20.03
 +
Serviço #692 @ 6.37
 +
09:06 Évora
 +
09:17 Casa Branca
 +
09:31 Vendas Novas
 +
09:53 Pinhal Novo
 +
Serviço #570 @ 13.66
 +
10:48 Pinhal Novo
 +
11:19 Grândola
 +
11:33 Ermidas-Sado
 +
11:55 Funcheira
 +
12:24 Santa Clara-Sabóia
 +
12:52 Messines-Alte
 +
13:02 Tunes
 +
 
 +
Itinerário 2 para 2017-10-25 @ 22.57
 +
Serviço #184 @ 16.41
 +
15:35 Tunes
 +
17:23 Pinhal Novo
 +
Serviço #596 @ 6.17
 +
17:48 Pinhal Novo
 +
18:10 Vendas Novas
 +
18:24 Casa Branca
 +
18:35 Évora
 +
</text>
 +
}}
 +
 
 +
=== Registar itinerário para um passageiro ===
 +
 
 +
Esta acção permite registar um novo itinerário para um passageiro.
 +
 
 +
Para tal, pede-se o identificador do passageiro ('''requestPassengerId()'''), a nome da estação de partida ('''requestDepartureStationName()'''), o nome da estação de destino ('''requestArrivalStationName()'''), a data da partida, no formato [https://en.wikipedia.org/wiki/ISO_8601 YYYY-MM-DD] ('''requestDepartureDate()'''), e a hora mínima para a partida, no formato HH:MM ('''requestDepartureTime()'''). 
 +
 
 +
Se a data não estiver num formato correcto, é lançada a excepção '''mmt.app.BadDateException''', não sendo realizada nenhuma acção.
 +
 
 +
Se a hora não estiver num formato correcto, é lançada a excepção '''mmt.app.BadTimeException''', não sendo realizada nenhuma acção.
 +
 
 +
Se não for possível fazer uma reserva, i.e., o número de candidatos é nulo, é apresentada a mensagem '''noItinerariesBetween()''', não sendo realizada nenhuma acção.
 +
 
 +
Em caso de sucesso, como resultado, o sistema apresenta uma lista com todos os candidatos encontrados (ordenados hierarquicamente pelos seguintes critérios: hora de partida, hora de chegada e tempo de viagem). Se forem encontradas múltiplas alternativas com o mesmo serviço de partida, apenas se apresenta a que chega mais cedo (excepto se existir uma ligação directa nesse serviço, mesmo que mais lenta). De seguida, o sistema pede o número da alternativa ('''requestItineraryChoice()''') a guardar (1 para a primeira alternativa, etc.) no registo dos itinerários adquiridos pelo passageiro, actualizando-se o estado correspondente.
 +
 
 +
Se a escolha for 0 (zero), não é realizada nenhuma acção e os itinerários candidatos são descartados.
 +
 
 +
Se a escolha corresponder a um número de itinerário inválido, é lançada a excepção '''mmt.app.NoSuchItineraryException''', não sendo realizada nenhuma acção (são descartados eventuais itinerários candidatos).
 +
 
 +
O formato de apresentação das escolhas é como definido para os itinerários, mas sem a linha relativa ao passageiro.
 +
 
 +
{{CollapsedCode|Exemplo de apresentação de hipótese de itinerário: Évora >> Tunes (opção #3)|
 +
<text>
 +
Itinerário 3 para 2017-11-21 @ 20.38
 +
Serviço #694 @ 6.65
 +
16:57 Évora
 +
17:08 Casa Branca
 +
17:22 Vendas Novas
 +
17:29 São João das Craveiras
 +
17:33 Pegoes
 +
17:38 Fernando Pó
 +
17:43 Poceirão
 +
17:51 Pinhal Novo
 +
Serviço #574 @ 13.73
 +
18:18 Pinhal Novo
 +
18:46 Grândola
 +
19:01 Ermidas-Sado
 +
19:29 Funcheira
 +
19:57 Santa Clara-Sabóia
 +
20:24 Messines-Alte
 +
20:34 Tunes
 +
</text>
 +
}}
 +
 
 +
= Leitura de Dados a Partir de Ficheiros Textuais =
 
   
 
   
Além das opções de manipulação de ficheiros descritas no [[#Salvaguarda do Documento Actual|menu principal]], é possível iniciar a aplicação com um ficheiro de texto especificado pela propriedade Java '''[[#Execução dos Programas e Testes Automáticos|import]]'''. Este ficheiro contém um programa que é avaliado pelo interpretador. <!-- Se não existirem definições de variáveis, a avaliação não deixa traços na memória da calculadora. Apenas é lida a primeira expressão presente no ficheiro.--> O programa fica registado com o nome '''import'''.
+
Além das opções de manipulação de ficheiros descritas no [[#Salvaguarda do Documento Actual|menu principal]], é possível iniciar a aplicação com um ficheiro de texto especificado pela propriedade Java '''[[#Execução dos Programas e Testes Automáticos|import]]'''. <!--Este ficheiro contém um programa que é avaliado pelo interpretador. Se não existirem definições de variáveis, a avaliação não deixa traços na memória da calculadora. Apenas é lida a primeira expressão presente no ficheiro. O programa fica registado com o nome '''import'''.-->
  
 
== Exemplo de ficheiro a importar ==
 
== Exemplo de ficheiro a importar ==
  
Este programa é composto por três expressões, a última das quais é um ciclo.
+
O formato é como exemplificado a seguir. A notação "..." significa repetição de formato. Nos itinerários, o primeiro número indica o identificador único do passageiro e as outras entradas (a seguir à data) indicam o serviço e as estações correspondentes de início e fim do troço que faz parte do itinerário.
<lisp>
+
<source lang="text">
(seq (add 1 2) (sub 1 2) (mul (add 1 2) 3))
+
SERVICE|180|51.5|05:47|Porto - Campanhã|...|11:23|Faro
(seq (add 1 2) (sub 1 2) (mul (add 1 2) 3) (div 2 5) "olá")
+
SERVICE|694|12.2|16:57|Évora|17:08|Casa Branca|...|18:36|Lisboa - Oriente
(seq (set ix 0) (while (lt ix 30) (seq (print "ix =" ix) (set ix (add ix 1)))))
+
SERVICE|420|11.45|08:36|Valença|09:09|Viana do Castelo|09:50|Nine|10:18|Porto - Campanhã
</lisp>
+
SERVICE|5500|11.35|15:48|Elvas|...|18:25|Entroncamento
 
+
PASSENGER|Obi-Wan
Embora o interpretador de expressões tenha de assinalar problemas relativos à interpretação de expressões mal especificadas, assume-se que não existem entradas mal-formadas nestes ficheiros (embora tenham de ser detectadas).
+
PASSENGER|Yoda
 +
ITINERARY|0|2017-10-18|690/Évora/Pinhal Novo|180/Pinhal Novo/Tunes|5904/Tunes/Silves
 +
</source>
 +
<!--
 +
Embora o interpretador de expressões tenha de assinalar problemas relativos à interpretação de expressões mal especificadas, a-->Assume-se que não existem entradas mal-formadas nestes ficheiros.<!-- (embora tenham de ser detectadas).-->
  
 
= Considerações sobre Flexibilidade e Eficiência =
 
= Considerações sobre Flexibilidade e Eficiência =
 
   
 
   
Devem ser possíveis extensões ou alterações de funcionalidade com impacto mínimo no código já produzido para a aplicação. O objectivo é aumentar a flexibilidade da aplicação relativamente ao suporte de novas funções. Em particular, a solução encontrada para salvaguardar textualmente o conteúdo do documento deve ser suficientemente flexível de modo a permitir visualizar o conteúdo de um documento noutro formato (por exemplo, XML) sem que isso implique alterações no código ''core'' da aplicação.
+
Devem ser possíveis extensões ou alterações de funcionalidade com impacto mínimo no código já produzido para a aplicação. O objectivo é aumentar a flexibilidade da aplicação relativamente ao suporte de novas funções. Assim:
 +
* A aplicação deve permitir a adição de novas categorias de passageiro sem alterações no código já realizado.
 +
* A aplicação deve permitir a adição de novas procuras de serviços com baixo impacto no código já realizado (em termos do código para suportar a nova procura).
  
 
= Execução dos Programas e Testes Automáticos =
 
= Execução dos Programas e Testes Automáticos =
Line 223: Line 359:
  
 
Este comando não deve produzir qualquer resultado quando os ficheiros são iguais. Note-se, contudo, que este teste não garante o correcto funcionamento do código desenvolvido, apenas verificando alguns aspectos da sua funcionalidade.
 
Este comando não deve produzir qualquer resultado quando os ficheiros são iguais. Note-se, contudo, que este teste não garante o correcto funcionamento do código desenvolvido, apenas verificando alguns aspectos da sua funcionalidade.
 +
 +
= Notas de Implementação =
 +
 +
Tal como indicado acima, algumas classes fornecidas como [[Programação com Objectos/Projecto de Programação com Objectos/Material de Apoio ao Desenvolvimento|material de apoio]], são de uso obrigatório e não podem ser alteradas. Outras dessas classes são de uso obrigatório e têm de ser alteradas.
 +
 +
A serialização Java usa as classes da ''package'' '''[https://docs.oracle.com/javase/8/docs/api/java/io/package-summary.html java.io]''', em particular, a interface '''[https://docs.oracle.com/javase/8/docs/api/java/io/Serializable.html java.io.Serializable]''' e as classes de leitura '''[https://docs.oracle.com/javase/8/docs/api/java/io/ObjectInputStream.html java.io.ObjectInputStream]''' e escrita '''[https://docs.oracle.com/javase/8/docs/api/java/io/ObjectOutputStream.html java.io.ObjectOutputStream]''' (entre outras).
 +
 +
A representação e manipulação de datas e tempos deve ser realizada através das classes da ''package'' '''[https://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html java.time]''', em particular, através das classes '''[https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html java.time.LocalDate]''' e '''[https://docs.oracle.com/javase/8/docs/api/java/time/LocalTime.html java.time.LocalTime]'''. Diferenças entre tempos são representadas pela classe '''[https://docs.oracle.com/javase/8/docs/api/java/time/Duration.html java.time.Duration]'''.
  
 
[[category:Ensino]]
 
[[category:Ensino]]

Latest revision as of 14:26, 1 August 2019

AVISOS - Avaliação em Época Normal

Esclarecimento de dúvidas:

  • Consultar sempre o corpo docente atempadamente: presencialmente ou através do endereço oficial da disciplina [1].
  • Não utilizar fontes de informação não oficialmente associadas ao corpo docente (podem colocar em causa a aprovação à disciplina).
  • Não são aceites justificações para violações destes conselhos: quaisquer consequências nefastas são da responsabilidade do aluno.

Requisitos para desenvolvimento, material de apoio e actualizações do enunciado (ver informação completa em Projecto de Programação com Objectos):

  • O material de apoio é de uso obrigatório e não pode ser alterado.
  • Verificar atempadamente (mínimo de 48 horas antes do final de cada prazo) os requisitos exigidos pelo processo de desenvolvimento.

Processo de avaliação (ver informação completa em Avaliação do Projecto):

  • Datas: 2017/10/18 12:00 (inicial); 2017/11/20 12:00 (intercalar); 2017/12/11 12:00 (final); 2017/12/11-2017/12/15 (teste prático).
  • Todas as entregas são cruciais para o bom desenvolvimento do projecto, sendo obrigatórias: a não realização de uma entrega implica a exclusão da avaliação do projecto e, por consequência, da avaliação da disciplina.
  • Verificar atempadamente (até 48 horas antes do final de cada prazo) os requisitos exigidos pelo processo de avaliação, incluindo a capacidade de acesso ao repositório.
  • Apenas se consideram para avaliação os projectos existentes no repositório oficial. Apenas se considera para avaliação o ramo 'main'.
  • Trabalhos não presentes no repositório no final do prazo têm classificação 0 (zero) (não são aceites outras formas de entrega). Não são admitidas justificações para atrasos em sincronizações do repositório. A indisponibilidade temporária do repositório ou de outros materiais, desde que inferior a 24 horas, não justifica atrasos na submissão de um trabalho.
  • A avaliação do projecto pressupõe o compromisso de honra de que o trabalho correspondente foi realizado pelos alunos correspondentes ao grupo de avaliação.
  • Fraudes na execução do projecto terão como resultado a exclusão dos alunos implicados do processo de avaliação.
Material de Uso Obrigatório
As bibliotecas po-uuilib e o conteúdo inicial do CVS são de uso obrigatório:
  • po-uuilib (classes de base) po-uuilib-201708311009.tar.bz2 (não pode ser alterada) - javadoc
  • mmt-core (classes do "core") (via CVS) (deve ser completada -- os nomes das classes fornecidas não podem ser alterados)
  • mmt-app (classes de interacção) (via CVS) (deve ser completada -- os nomes das classes fornecidas não podem ser alterados)
A máquina virtual, fornecida para desenvolvimento do projecto, já contém todo o material de apoio.
Uso Obrigatório: Repositório CVS
Apenas se consideram para avaliação os projectos existentes no repositório CVS oficial.

Trabalhos não presentes no repositório no final do prazo têm classificação 0 (zero) (não são aceites outras formas de entrega). Não são admitidas justificações para atrasos em sincronizações do repositório. A indisponibilidade temporária do repositório, desde que inferior a 24 horas, não justifica atrasos na submissão de um trabalho.

O objectivo do projecto é criar uma aplicação que gere os itinerários de passageiros que usam comboios.

Os itinerários são constituídos por um ou mais segmentos, realizados por comboios que cumprem serviços pré-definidos.

Neste texto, o tipo negrito indica um literal (i.e., é exactamente como apresentado); o símbolo indica um espaço; e o tipo itálico indica uma parte variável (i.e., uma descrição).

Estrutura dos serviços, dos passageiros e dos itinerários

Existem três conceitos básicos: serviços, passageiros e itinerários.

Estrutura de um serviço

Um serviço é um percurso realizado por um comboio ao longo de várias estações. A cada estação de um serviço está associado o momento de partida do comboio. Assume-se que todos os serviços se realizam em todos os dias. Além das informações anteriores, o serviço tem ainda um identificador numérico único (definido na criação do serviço).

Exemplo de serviço: Serviço 850: Valença >> Porto - Campanhã
  Serviço #850 - 10.95
  07:36 Valença
  07:47 Vila Nova de Cerveira
  07:56 Caminha
  08:03 Âncora Praia
  08:16 Viana do Castelo
  08:20 Areia-Darque
  08:30 Barroselas
  08:37 Tamel
  08:53 Barcelos
  09:05 Nine
  09:13 Famalicão
  09:21 Trofa
  09:35 Ermesinde
  09:45 Porto - Campanhã

Cada segmento de um serviço tem um custo base, que depende da distância entre estações. A distância entre estações corresponde ao tempo gasto na viagem entre essas estações. Assim, considerando que a totalidade do custo do serviço 850 (no exemplo acima) é de €10.95 (para 129 minutos), o custo do segmento Viana do Castelo >> Nine (para 49 minutos) é de 10.95 * 49 / 129 = €4.16.

O preço pago pelo passageiro depende ainda da sua categoria, que pode ser normal ou outra (a adição de novas categorias deve ter impacto reduzido no código da aplicação).

Considera-se que apenas existem as estações que aparecem em, pelo menos, um serviço.

Estrutura de um passageiro

Um passageiro tem um identificador único e um nome (cadeia de caracteres). O identificador único é atribuído automaticamente e de forma sequencial no momento do registo do passageiro, sendo atribuído o identificador 0 (zero) ao primeiro passageiro.

Os passageiros são categorizados como normais, frequentes ou especiais. Um passageiro é considerado frequente enquanto o valor dos últimos 10 itinerários for superior a €250. Volta a ser normal se esta condição deixar de se verificar. Um cliente é considerado especial enquanto tiver gasto mais de €2500 nos últimos 10 itinerários. Deixa de ser especial se esta condição deixar de se verificar. Se se verificarem simultâneamente as condições para ser um cliente frequente e especial, o cliente é considerado especial. Aplicam-se as seguintes condições para atribuição de descontos em itinerários: passageiros normais, 0%; passageiros frequentes, 15%; passageiros especiais, 50%.

Estrutura de um itinerário

Um itinerário corresponde a uma viagem realizada num dado dia, com base num conjunto de segmentos lógicos, correspondentes a partes de serviços, i.e., partidas (ou chegadas) agendadas para cada estação. É possível saber, através do serviço associado, qual o percurso a realizar nesse segmento.

O custo base do itinerário é a soma dos custos parciais de cada serviço que o constitui, correspondentes aos segmentos realizados nesses serviços. Dependendo da categoria do passageiro, podem ser realizados descontos.

Os itinerários são identificados por um número de ordem no contexto de cada passageiro. Não é possível remover itinerários já registados.

Exemplo de itinerário: Évora >> Loulé

<text> Itinerário 3 para 2017-10-18 @ 25.84 Serviço #690 @ 6.37 07:06 Évora 07:17 Casa Branca 07:31 Vendas Novas 07:53 Pinhal Novo Serviço #180 @ 19.47 09:06 Pinhal Novo 10:54 Tunes 11:01 Albufeira - Ferreiras 11:13 Loulé </text>

Funcionalidade da aplicação

A aplicação permite manter informação sobre serviços. Permite ainda registar e gerir passageiros e criar novos itinerários para eles. Possui ainda a capacidade de preservar o seu estado (não é possível manter várias versões do estado da aplicação em simultâneo).

A base de dados de serviços é carregada no início da aplicação. Não é possível adicionar ou remover serviços durante a execução da aplicação.

Note-se que não é necessário implementar de raiz a aplicação: já existem classes que representam e definem a interface geral da funcionalidade do core da aplicação, tal como é visível pelos comandos da aplicação.
A interface geral do core já está parcialmente implementada na classe mmt.TicketOffice e outras fornecdidas (cujos nomes devem ser mantidos), devendo ser adaptadas onde necessário. É ainda necessário criar e implementar as restantes classes que suportam a operação da aplicação.

Consultas de Serviços

É possível fazer várias consultas sobre serviços (ver também abaixo). É possível ver todos os serviços, inspeccionar um serviço particular e inspeccionar serviços com base na especificação de estações. Podem ser definidas, no futuro, outras formas de pesquisa e de apresentação de serviços.

Manipulação de Passageiros

A aplicação permite realizar várias operações sobre passageiros. É possível obter a lista completa de passageiros conhecidos, assim como informação detalhada sobre passageiros individuais. É ainda possível registar novos passageiros e alterar o nome dos passageiros.

Manipulação de Itinerários

A aplicação permite obter informações sobre todos os itinerários já comprados ou, em pormenor, sobre os itinerários de passageiros específicos. Permite ainda criar novos itinerários.

Os passageiros são creditados com o valor monetário de cada itinerário comprado e com o tempo decorrido nesses itinerários (medido entre os pontos extremos).

Serialização

É possível reiniciar (ou seja, mantendo a informação sobre serviços, eliminar a informação sobre passageiros e itinerários), guardar e recuperar o estado actual da aplicação, preservando todos a informação sobre serviços, passageiros e itinerários.

Interacção com o utilizador

Descreve-se nesta secção a funcionalidade máxima da interface com o utilizador. Em geral, os comandos pedem toda a informação antes de proceder à sua validação (excepto onde indicado). Todos os menus têm automaticamente a opção Sair (fecha o menu).

As operações de pedido e apresentação de informação ao utilizador devem realizar-se através dos objectos form e display, respectivamente, presentes em cada comando. As mensagens são produzidas pelos métodos das bibliotecas de suporte (po-uuilib e mmt-app). As mensagens não podem ser usadas no núcleo da aplicação (mmt-core). Além disso, não podem ser definidas novas. Potenciais omissões devem ser esclarecidas antes de qualquer implementação.

As excepções usadas na interacção, excepto se indicado, são subclasses de pt.tecnico.po.ui.DialogException, são lançadas pelos comandos e tratadas por pt.tecnico.po.ui.Menu. Outras excepções não devem substituir as fornecidas nos casos descritos.

Note-se que o programa principal e os comandos e menus, a seguir descritos, já estão parcialmente implementados nas packages mmt.app, mmt.app.main, mmt.app.service, mmt.app.passenger e mmt.app.itinerary. Estas classes são de uso obrigatório e estão disponíveis no CVS (módulo mmt-app).

Menu Principal

As acções do menu permitem gerir a salvaguarda do estado da aplicação e abrir submenus. A lista completa é a seguinte: Reiniciar, Abrir, Guardar e Consulta de Serviços, Manipulação de Passageiros e Manipulação de Itinerários. Inicialmente, a aplicação apenas tem informação sobre os serviços que foram carregados no arranque.

As etiquetas das opções deste menu estão definidas na classe mmt.app.main.Label. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe mmt.app.main.Message.

Estes comandos já estão implementados nas classes da package mmt.app.main (disponível no CVS), respectivamente: DoReset, DoOpen, DoSave, DoOpenServicesMenu, DoOpenPassengersMenu, DoOpenItinerariesMenu.

Salvaguarda do estado actual da aplicação

O conteúdo da aplicação (inclui todos os serviços, passageiros e itinerários actualmente em memória) pode ser guardado para posterior recuperação (via serialização Java: java.io.Serializable). Na leitura e escrita do estado da aplicação, devem ser tratadas as excepções associadas. A funcionalidade é a seguinte:

  • Reiniciar -- Reinicia a aplicação: destrói toda a informação sobre passageiros e itinerários; mantém toda a informação sobre serviços.
  • Abrir -- Carrega os dados de uma sessão anterior a partir de um ficheiro (ficando associado à aplicação). A informação a carregar compreende serviços, passageiros e itinerários. Pede-se o nome do ficheiro a abrir (openFile()). Caso o ficheiro não exista, é apresentada a mensagem fileNotFound().
  • Guardar -- Guarda o estado actual da aplicação (serviços, passageiros e itinerários) no ficheiro associado. Se não existir associação, pede-se o nome do ficheiro a utilizar, ficando a ele associado. Esta interacção realiza-se através do método newSaveAs(). Não é executada nenhuma acção se não existirem alterações desde a última salvaguarda.

As opções Reiniciar e Abrir substituem a informação na aplicação nas condições indicadas acima.

A opção Sair nunca guarda o estado da aplicação, mesmo que existam alterações.

Gestão e consulta de dados da aplicação

  • Consulta de Serviços -- Abre o menu de consulta de serviços (se existirem serviços registados).
  • Gestão de Passageiros -- Abre o menu de gestão de passageiros (se existirem passageiros registados).
  • Gestão de Itinerários -- Abre o menu de gestão de itinerários (se existirem itinerários registados).

Menu de Consulta de Serviços

Este menu permite efectuar consultas sobre a base de dados de serviços. A lista completa é a seguinte: Mostrar todos os serviços, Mostrar serviço com um dado número, Mostrar serviços com origem numa estação dada, Mostrar serviços com término numa estação dada.

As etiquetas das opções deste menu estão definidas na classe mmt.app.service.Label. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe mmt.app.service.Message.

Sempre que for pedido o identificador de um serviço (requestServiceId()) e o serviço não existir, é lançada a excepção mmt.app.NoSuchServiceException. Sempre que for pedido o nome de uma estção (requestStationName()) e a estação não existir, é lançada a excepção mmt.app.NoSuchStationException.

Estes comandos já estão implementados nas classes da package mmt.app.service (disponível no CVS), respectivamente: DoShowAllServices, DoShowServiceByNumber, DoShowServicesDepartingFromStation, DoShowServicesArrivingAtStation.

Mostrar todos os serviços

Apresenta informações sobre todos os serviços conhecidos. A lista é ordenada pelo identificador do serviço e o formato é o descrito em Mostrar serviço com um dado número.

Mostrar serviço com um dado número

Apresenta informações sobre um serviço. Depois de pedir o identificador do serviço (requestServiceId()), apresenta a informação sobre o serviço, tal como indicado no formato seguinte, para um serviço com N paragens (HH;MM representa o tempo em horas e minutos). O preço deve ser apresentado com duas casas decimais.

Serviço #número-do-serviço @ preço
HH:MM nome-de-estação-1
...
HH:MM nome-de-estação-N
Exemplo de apresentação de serviço: Serviço 180: Porto >> Faro

<text> Serviço #180 @ 51.50 05:47 Porto - Campanhã 05:52 Vila Nova de Gaia-Devesas 06:21 Aveiro 06:46 Coimbra-B 08:23 Lisboa - Oriente 08:31 Lisboa - Entrecampos 09:06 Pinhal Novo 10:54 Tunes 11:01 Albufeira - Ferreiras 11:13 Loulé 11:23 Faro </text>

Mostrar serviços com origem numa estação dada

Apresenta os números e estações terminais dos serviços com origem na estação indicada como resposta a requestStationName(), ordenado por hora de partida do serviço.

O formato de apresentação é o descrito em Mostrar serviço com um dado número.

Mostrar serviços com término numa estação dada

Apresenta os números e estações de origem dos serviços com término na estação indicada como resposta a requestStationName(), ordenado por hora de chegada do serviço.

O formato de apresentação é o descrito em Mostrar serviço com um dado número.

Menu de Gestão de Passageiros

Este menu permite efectuar operações sobre um passageiro. A lista completa é a seguinte: Mostrar passageiros, Mostrar passageiro, Registar passageiro, Alterar nome de passageiro.

As etiquetas das opções deste menu estão definidas na classe mmt.app.passenger.Label. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe mmt.app.passenger.Message.

Sempre que for pedido o identificador de um passageiro (requestPassengerId()) e o passageiro não existir, é lançada a excepção mmt.app.NoSuchPassengerException.

Estes comandos já estão implementados nas classes da package mmt.app.passenger (disponível no CVS), respectivamente: DoShowAllPassengers, DoShowPassengerById, DoRegisterPassenger, DoChangePassengerName.

Mostrar passageiros

Apresenta informações sobre todos os passageiros conhecidos. A lista é ordenada pelo identificador e o formato é o descrito em Mostrar passageiro.

Mostrar passageiro

Apresenta informações sobre um passageiro. Depois de pedir o identificador do passageiro (requestPassengerId()), apresenta o nome, a categoria do passageiro (NORMAL, FREQUENTE, ESPECIAL), o número de itinerários adquiridos, o tempo acumulado (formato HH:MM) correspondente aos itinerários adquiridos e o valor pago por esses itinerários (o valor pago deve ser apresentado com duas casas decimais).

Formato:

id | nome | categoria | número-de-itinerários | valor-pago | tempo-acumulado

Exemplo:

1234|John Doe|NORMAL|123|9876.54|123:45

Registar passageiro

Permite registar um passageiro no sistema. É pedido o nome do passageiro (requestPassengerName()). O novo passageiro é categorizado como NORMAL.

Alterar nome de passageiro

Permite alterar o nome do passageiro. Para tal, pede-se o identificador do passageiro cujo nome deve ser alterado (requestPassengerId()) e o novo nome (requestPassengerName()).

Menu de Gestão de Itinerários

Este menu permite efectuar operações sobre itinerários. A lista completa é a seguinte: Mostrar todos os itinerários, Mostrar itinerários associados a um passageiro, Registar itinerário para um passageiro.

As etiquetas das opções deste menu estão definidas na classe mmt.app.itinerary.Label. Todos os métodos correspondentes às mensagens de diálogo para este menu estão definidos na classe mmt.app.itinerary.Message.

Estes comandos já estão parcialmente implementados nas classes da package mmt.app.passenger (disponível no CVS), respectivamente: DoShowAllItineraries, DoShowPassengerItineraries, DoRegisterItinerary, DoShowTotalValue, DoShowServiceHistograms.

Mostrar todos os itinerários

Apresenta todos os itinerários registados para todos os passageiros. O formato de apresentação é como para os serviços, mas apenas se apresenta o segmento percorrido e o respectivo custo. A informação é apresentada para cada passageiro, sendo os passageiros apresentados por ordem crescente do seu número de identificação.

O formato de apresentação é como definido para a apresentação dos itinerários de um passageiro, mas apresenta-se a informação relativamente a todos os passageiros.

Note-se que a apresentação de itinerários apenas tem significado quando existem itinerários para mostrar, i.e., não se devem apresentar passageiros que não têm itinerários.

Mostrar itinerários associados a um passageiro

Apresenta os itinerários do passageiro cujo identificador é pedido através de requestPassengerId(). A lista é ordenada pela data dos itinerários.

Se o passageiro não tiver itinerários, deve ser apresentada a mensagem noItineraries().

O formato de apresentação é análogo ao dos serviços, iniciado com uma linha com o identificador do passageiro, à qual se segue a lista de itinerários, cada um precedido da linha com o número de ordem do itinerário (no contexto de cada passageiro) e da data correspondente:

 == Passageiro identificador-do-passageiro: nome-do-passageiro ==
 Itinerário número-de-ordem-do-itinerário para data @ preço-total-do-itinerário

O preço associado a cada serviço no itinerário é o da fracção do tempo do serviço nesse itinerário, com duas casas decimais. O preço do itinerário é a soma (sem arredondamentos) dos preços dos segmentos que o formam, apresentado com duas casas decimais.

Exemplo de apresentação de itinerários para um passageiro

<text>

Passageiro 2: José Simão

Itinerário 1 para 2017-10-18 @ 20.03 Serviço #692 @ 6.37 09:06 Évora 09:17 Casa Branca 09:31 Vendas Novas 09:53 Pinhal Novo Serviço #570 @ 13.66 10:48 Pinhal Novo 11:19 Grândola 11:33 Ermidas-Sado 11:55 Funcheira 12:24 Santa Clara-Sabóia 12:52 Messines-Alte 13:02 Tunes

Itinerário 2 para 2017-10-25 @ 22.57 Serviço #184 @ 16.41 15:35 Tunes 17:23 Pinhal Novo Serviço #596 @ 6.17 17:48 Pinhal Novo 18:10 Vendas Novas 18:24 Casa Branca 18:35 Évora </text>

Registar itinerário para um passageiro

Esta acção permite registar um novo itinerário para um passageiro.

Para tal, pede-se o identificador do passageiro (requestPassengerId()), a nome da estação de partida (requestDepartureStationName()), o nome da estação de destino (requestArrivalStationName()), a data da partida, no formato YYYY-MM-DD (requestDepartureDate()), e a hora mínima para a partida, no formato HH:MM (requestDepartureTime()).

Se a data não estiver num formato correcto, é lançada a excepção mmt.app.BadDateException, não sendo realizada nenhuma acção.

Se a hora não estiver num formato correcto, é lançada a excepção mmt.app.BadTimeException, não sendo realizada nenhuma acção.

Se não for possível fazer uma reserva, i.e., o número de candidatos é nulo, é apresentada a mensagem noItinerariesBetween(), não sendo realizada nenhuma acção.

Em caso de sucesso, como resultado, o sistema apresenta uma lista com todos os candidatos encontrados (ordenados hierarquicamente pelos seguintes critérios: hora de partida, hora de chegada e tempo de viagem). Se forem encontradas múltiplas alternativas com o mesmo serviço de partida, apenas se apresenta a que chega mais cedo (excepto se existir uma ligação directa nesse serviço, mesmo que mais lenta). De seguida, o sistema pede o número da alternativa (requestItineraryChoice()) a guardar (1 para a primeira alternativa, etc.) no registo dos itinerários adquiridos pelo passageiro, actualizando-se o estado correspondente.

Se a escolha for 0 (zero), não é realizada nenhuma acção e os itinerários candidatos são descartados.

Se a escolha corresponder a um número de itinerário inválido, é lançada a excepção mmt.app.NoSuchItineraryException, não sendo realizada nenhuma acção (são descartados eventuais itinerários candidatos).

O formato de apresentação das escolhas é como definido para os itinerários, mas sem a linha relativa ao passageiro.

Exemplo de apresentação de hipótese de itinerário: Évora >> Tunes (opção #3)

<text> Itinerário 3 para 2017-11-21 @ 20.38 Serviço #694 @ 6.65 16:57 Évora 17:08 Casa Branca 17:22 Vendas Novas 17:29 São João das Craveiras 17:33 Pegoes 17:38 Fernando Pó 17:43 Poceirão 17:51 Pinhal Novo Serviço #574 @ 13.73 18:18 Pinhal Novo 18:46 Grândola 19:01 Ermidas-Sado 19:29 Funcheira 19:57 Santa Clara-Sabóia 20:24 Messines-Alte 20:34 Tunes </text>

Leitura de Dados a Partir de Ficheiros Textuais

Além das opções de manipulação de ficheiros descritas no menu principal, é possível iniciar a aplicação com um ficheiro de texto especificado pela propriedade Java import.

Exemplo de ficheiro a importar

O formato é como exemplificado a seguir. A notação "..." significa repetição de formato. Nos itinerários, o primeiro número indica o identificador único do passageiro e as outras entradas (a seguir à data) indicam o serviço e as estações correspondentes de início e fim do troço que faz parte do itinerário.

SERVICE|180|51.5|05:47|Porto - Campanhã|...|11:23|Faro
SERVICE|694|12.2|16:57|Évora|17:08|Casa Branca|...|18:36|Lisboa - Oriente
SERVICE|420|11.45|08:36|Valença|09:09|Viana do Castelo|09:50|Nine|10:18|Porto - Campanhã
SERVICE|5500|11.35|15:48|Elvas|...|18:25|Entroncamento
PASSENGER|Obi-Wan
PASSENGER|Yoda
ITINERARY|0|2017-10-18|690/Évora/Pinhal Novo|180/Pinhal Novo/Tunes|5904/Tunes/Silves

Assume-se que não existem entradas mal-formadas nestes ficheiros.

Considerações sobre Flexibilidade e Eficiência

Devem ser possíveis extensões ou alterações de funcionalidade com impacto mínimo no código já produzido para a aplicação. O objectivo é aumentar a flexibilidade da aplicação relativamente ao suporte de novas funções. Assim:

  • A aplicação deve permitir a adição de novas categorias de passageiro sem alterações no código já realizado.
  • A aplicação deve permitir a adição de novas procuras de serviços com baixo impacto no código já realizado (em termos do código para suportar a nova procura).

Execução dos Programas e Testes Automáticos

Usando os ficheiros test.import, test.in e test.out, é possível verificar automaticamente o resultado correcto do programa. Note-se que é necessária a definição apropriada da variável CLASSPATH (ou da opção equivalente -cp do comando java), para localizar as classes do programa, incluindo a que contém o método correspondente ao ponto de entrada da aplicação (mmt.app.App.main). As propriedades são tratadas automaticamente pelo código de apoio.

       java -Dimport=test.import -Din=test.in -Dout=test.outhyp mmt.app.App

Assumindo que aqueles ficheiros estão no directório onde é dado o comando de execução, o programa produz o ficheiro de saída test.outhyp. Em caso de sucesso, os ficheiros das saídas esperada (test.out) e obtida (test.outhyp) devem ser iguais. A comparação pode ser feita com o comando:

        diff -b test.out test.outhyp

Este comando não deve produzir qualquer resultado quando os ficheiros são iguais. Note-se, contudo, que este teste não garante o correcto funcionamento do código desenvolvido, apenas verificando alguns aspectos da sua funcionalidade.

Notas de Implementação

Tal como indicado acima, algumas classes fornecidas como material de apoio, são de uso obrigatório e não podem ser alteradas. Outras dessas classes são de uso obrigatório e têm de ser alteradas.

A serialização Java usa as classes da package java.io, em particular, a interface java.io.Serializable e as classes de leitura java.io.ObjectInputStream e escrita java.io.ObjectOutputStream (entre outras).

A representação e manipulação de datas e tempos deve ser realizada através das classes da package java.time, em particular, através das classes java.time.LocalDate e java.time.LocalTime. Diferenças entre tempos são representadas pela classe java.time.Duration.