Issue Management

Si sviluppi un programma che consenta ad un'azienda di trattare le segnalazioni di anomalie relative ai componenti software che vende. La classe principale si chiama IssueManager; tutte le classi si trovano nel package ticketing. La classe Example presenta esempi di uso dei metodi principali.
Le eccezioni lanciate dal programma sono di tipo TicketException.

La JDK documentation si trova sul server locale.

R1: Users

Il sistema ammette due ruoli di utenti: Reporter and Maintainer. Un utente può svolgere un solo ruolo o entrambi.

Il metodo createUser() riceve uno username e il set dei ruoli (UserClasses) che l'utente svolge.
In alternativa al set si può usare una lista variabile di argomenti.
In entrambi i casi il metodo lancia un'eccezione se lo username è già stato inserito o se nessun ruolo è indicato.

Dato uno username si può ottenere il set dei ruoli dell'utente corrispondente con il metodo getUserClasses().

R2: Components

I componenti forniti dall'azienda sono costituiti ricorsivamente da sotto-componenti.

Il metodo defineComponent() genera un nuovo componente dato il nome e lancia un'eccezione se esiste già un componente con quel nome.

System Sub A Sub B Sub C Sub B.1 Sub B.2

Il metodo defineSubComponent() genera un nuovo sotto-componente dati il nome e il path che identifica il predecessore (componente o sotto-componente) di cui il nuovo elemento diventa sotto-componente. Lancia un'eccezione se il predecessore non esiste o se ha già un sotto-componente con lo stesso nome.
Esempio: dato il sistema in figura, per aggiungere SubC al componente System si scrive: tm.defineSubComponent("SubC","/System");, mentre per aggiungere SubB.2 si scrive: tm.defineSubComponent("SubB.2","/System/SubB");.

Dato il path di un elemento (componente o sotto-componente) si può ottenere il set dei nomi semplici dei sotto-componenti con il metodo getSubComponents() e il path del predecessore con il metodo getParentComponent() (che dà null se l'elemento non ha un predecessore).

Si noti che i path iniziano con '/' e contengono la lista dei (sotto-)componenti, a partire dal componente di top-level, separati da '/'.

R3: Ticket opening

Un utente può aprire un ticket che contiene i dettagli di un'anomalia riguardante un dato elemento.

Un ticket è aperto con il metodo openTicket() che riceve lo username dell'utente, il path dell'elemento difettoso, la descrizione dell'anomalia e la severità (Severity) della stessa. Il metodo dà un id univoco (intero progressivo a partire da 1) per il ticket.
Lancia un'eccezione se lo username non è valido, il path non identifica alcun elemento, o se l'utente non svolge il ruolo Reporter.

Il metodo getTicket() riceve un id e dà l'oggetto Ticket corrispondente oppure null se l'id non è valido. Il metodo getAllTickets() dà la lista dei ticket ordinata naturalmente (si legga la nota).

La classe Ticket offre i metodi getter getDescription(), getId(), getComponent(), getAuthor() e getSeverity().

Nota: Gli oggetti che compongono un tipo enumerativo implementano automaticamente l'interfaccia Comparable; l'ordinamento naturale corrisponde all'ordine con cui gli oggetti sono definiti nel tipo enumerativo. Quindi nella Severity Blocking precede Major.

R4: Ticket lifecycle

I ticket hanno tre stati: Open, Assigned, Closed. Quando è aperto, un ticket è posto nello stato Open.

Il metodo assignTicket() riceve un ticket id e uno username: porta lo stato del ticket a Assigned e collega il ticket all'utente come assegnatario del ticket. Lancia un'eccezione se il ticket id o lo username non sono validi, o se l'utente non svolge il ruolo Maintainer.

Il metodo closeTicket() riceve un ticket id e la descrizione della soluzione, e porta lo stato del ticket a Closed. Lancia un'eccezione se il ticket non si trova nello stato Assigned.

La classe Ticket offre il metodo getter getState(), che dà lo stato corrente del ticket.

Il metodo getSolutionDescription() della classe Ticket dà la descrizione della soluzione; lancia un'eccezione se il ticket non si trova nello stato Closed.

R5: Statistics

Il metodo countBySeverityOfState() dato uno stato dei ticket fornisce una mappa con il numero di ticket per Severity, considerando soltanto i ticket in quello stato oppure tutti i ticket se l'argomento è nullo. La mappa è ordinata in base alla Severity.

Il metodo topMaintainers() dà una lista di stringhe: ogni stringa ha il formato "username:###" dove username è il nome dell'utente e ### è il numero di ticket chiusi dall'utente come maintainer. La lista è ordinata per numero decrescente di ticket e poi per username (in ordine alfabetico).