@startuml
hide empty members

namespace client {

  class ClientManager{
    - Long lastMail
    - User currentUser

    + ClientManager()
    + changeUser(User newUser): void
    + getAddressName(): String
    + sendMail(List<User> destList, String subject, String content): void
    + synchMails(): List<Mail>
    + removeMail(Mail mail): void
    + resetLastMail(): void
    + updateLastMail(List<Mail> mailList): void
    - startConnection(): ProtocolClient
  }

  ClientManager --> "data::User":- currentUser
}
namespace server {

  class SocketHandler {
    - Socket s
    - ProtocolServer protocolHandler
    - DataModel model
    - Logger logger

    + SocketHandler(Socket s, DataModel model)
    + void run(): void
  }

  class ServerManager {
    - DataModel model
    - Dispatcher dispatcher
    - boolean running
    - Logger logger

    + ServerManager()
    + start(): void
    + stop(): void
    + getModel(): DataModel
  }

  class Dispatcher {
    + final {static} int CONNECTION_TIMEOUT = 2000
    + final {static} int NUM_THREAD = 10
    + final {static} int AWAIT_TIME = 60
    - ExecutorService pool
    - DataModel model
    - volatile boolean shutdown
    - Logger logger
    - volatile ServerSocket welcomeSocket
    + Dispatcher(DataModel model) 
    + run(): void
    + shutdown(): void
  }

  Dispatcher --> "data::DataModel":- model
  Dispatcher --> "data::Logger":- logger
  ServerManager --> Dispatcher:- dispatcher
  ServerManager --> "data::DataModel" :- model
  ServerManager --> "data::Logger" :- logger 
  SocketHandler --> "protocol::ProtocolServer":- protocolHandler
  SocketHandler -up-> "data::DataModel":- model
  SocketHandler -up-> "data::Logger":- logger 
}

newpage
hide empty members
namespace data {

  class Config {
    + {static} int DEFAULT_PORT = 1234
    + {static} String DEFAULT_LISTENING_ADDRESS = "localhost"
    + {static} String DEFAULT_LOG_PATH = "log.bin"
    + {static} String DEFAULT_BASE_FOLDER = "base/"
    - int port
    - String listeningAddress
    - String logPath
    - String baseFolder
    - LogLevel logLevel
    + Config()
    + Config(int port, String listeningAddress, String logPath, String baseFolder)
    + getPort(): int
    + getListeningAddress(): String
    + getLogPath(): String
    + getBaseFolder(): String
    + getLogLevel(): LogLevel
    + setPort(int port): void
    + setListeningAddress(String listeningAddress): void
    + setLogPath(String logPath): void
    + setBaseFolder(String baseFolder): void
    + setLogLevel(LogLevel logLevel): void
    + loadConfig(): void
    + writeConfig(): void
    + loadDefault(): void
    + toString(): String
  }

  class IOManager {
    + {static}<T> Object load(String path)
    + {static}<T> void write(String path, T source)
    + {static}<T> void write(String folder, String name, T source) 
  }
  interface LoggerReceiver {
    + logEvent(String msg): void
  }

  class UserNotFoundException {
    + UserNotFoundException(User u)
  }

  class BaseId {
    - long id
    - String path

    + BaseId(String basePath)
    + synchronized newId(): long
    + synchronized resetId(): void
    + toString(): String
    - loadId(): void
    - writeId(): void
  }

  class LogLevel {
    + {static} final LogLevel ALL    
    + {static} final LogLevel DEBUG  
    + {static} final LogLevel CONN   
    + {static} final LogLevel INFO   
    + {static} final LogLevel WARN   
    + {static} final LogLevel ERROR  
    + {static} final LogLevel FATAL  
    + {static} final LogLevel SYSTEM 
    + {static} final LogLevel DEFAULT
    - int level
    - String label

    - LogLevel(int level, String label)
    + compareTo(LogLevel logLevel): int
    + {static} getAllLevels(): List<LogLevel>
    + toString(): String
    + equals(Object obj): boolean
  }

  class Logger {
    - ArrayList<LoggerReceiver> receivers

    + Logger(LogLevel logLevel)
    + Logger()
    + changeLogLevel(LogLevel newLevel): void
    + log(LogLevel logLevel, String msg): void
    + addReceiver(LoggerReceiver rec): void
    + removeReceiver(LoggerReceiver rec): void
  }

  class DataModel {
    - Config config
    - HashSet<User> users
    - HashMap<User, ArrayList<Mail>> mailBoxes
    - final {static} String CONFIG_PATH = "config.bin"
    - BaseId id
    - Logger logger

    + DataModel()
    + Config getConfig(): Config
    + getUsers(): HashSet<User> 
    + getMailBoxes(): HashMap<User, ArrayList<Mail>> 
    + getId(): BaseId 
    + getLogger(): Logger 
    + loadData(): void
    + sendMail(Mail mail): void
    + addMail(Mail mail, User user): void
    + removeMail(User user, Long id): void
    + createMailBox(User user): void
    + addUser(User user): void
    + removeUser(User user): void
    + writeUsers(): void
    + readUsers(): void
    + writeMailBox(User user): void
    + readMailBox(): void
    + getMails(User user, Long lastId): ArrayList<Mail>
    + userExists(User user): boolean
    + toString(): String
  }

  class User{
    - String address

    + User(String address)
    + getAddress(): String
    + setAddress(String address): void
    + toString(): String
    + hashCode(): int
    + equals(Object obj): boolean
  }

  class Mail{
    - Long id
    - String text
    - String subject
    - Date timestamp
    - User sender
    - ArrayList<User> destList
    - ArrayList<Long> previousList

    + Mail(Long id, User sender, ArrayList<User> destList, String text, 
    String subject, Date timestamp, ArrayList<Long> previousList)
    + Mail(ArrayList<User> destList, String subject, String text)
    + Mail(Long id)
    + getId():Long 
    + getText():String 
    + getSubject():String 
    + getTimestamp():Date 
    + getSender():User 
    + getDestList(): ArrayList<User> 
    + getPreviousList(): ArrayList<Long>
    + setId(Long id): void
    + setText(String text): void
    + setSubject(String subject): void
    + setTimestamp(Date timestamp): void
    + setSender(User sender): void
    + setDestList(ArrayList<User> destList): void
    + setPreviousList(ArrayList<Long> previousList): void
    + toString(): String 
    + hashCode(): int
    + equals(Object obj): boolean
  }

  class Error {
    + {static} final Error BAD_REQUEST
    + {static} final Error DEST_NOT_FOUND
    + {static} final Error INVALID_DATA
    + {static} final Error INTERNAL
    + {static} final Error NOT_IMPLEMENTED
    + {static} final Error UNKNOWN
    - int errorCode
    - String errorName

    - Error(int errorCode, String errorName)
    + int getCode(): int
    + getName(): String
    + toString(): String
    + equals(Object obj): boolean
  }

  Mail --> User: -sender
  Mail -->"0..N" User:- destList
  Config --> LogLevel:- logLevel
  DataModel --> Logger:- logger
  DataModel --> User
  DataModel --> Mail
  DataModel --> BaseId:- baseId
  DataModel --> Config:- config
  DataModel --> UserNotFoundException: throws
  Logger --> "0..N" LoggerReceiver:- receivers
}

'Inizio seconda immagine
newpage
hide empty members
namespace protocol{

  class ProtocolServer {
    - DataModel model
    - User clientUser
    - Logger logger
    + ProtocolServer(Socket s, DataModel model)
    + handle()
    - handleRequest(RequestToken t): void
    - handleSynchRequest(SynchToken t): void
    - handleRemoveMessageToken(RequestToken t): void
    - handleSendMessageToken(RequestToken t): void
  }

  class ProtocolClient {
    - boolean initialized = false
    + ProtocolClient(Socket s)
    + init(User u): void
    + synchRequest(): List<Mail>
    + synchRequest(Long id): List<Mail>
    + sendMessageRequest(Mail mail): void
    + sendRemoveMessageRequest(Long id): void
    - checkInitialization(): void
  }

  class ProtocolException {
    - ErrorToken errorToken
    + ProtocolException(String msg, ErrorToken token)
    + ProtocolException(String msg)
    + ProtocolException(String msg, Error error)
    + ErrorToken getErrorToken()
    + getStatus(): int
    + getError(): Error
    + toString(): String
  }

  abstract class ProtocolHandler {
    - ObjectOutputStream outStream
    - ObjectInputStream inStream
    - Socket s

    + ProtocolHandler(Socket s)
    + closeConnection(): void
    # sendToken(ProtocolToken t): void
    # receiveToken(): ProtocolToken
  }

  class RemoveMessageToken {
    - Long id
    + RemoveMessageToken(Long id)
    + getId(): Long
    + setId(Long id): void
  }

  class SendMessageToken {
    - Mail mail
    + SendMessageToken(Mail mail)
    + setMail(Mail mail): void
    + getMail(): Mail
  }

  class InitToken {
    - User u
    + InitToken(User u)
    + getUser(): User
    + setUser(User u): void
  }

  class SynchToken{
    - Long id
    + SynchToken(Long id)
    + getId(): Long
    + setId(Long id): void
  }

  class AckToken{
    + AckToken()
  }

  abstract class ResponseToken{
    + ResponseToken()
  }

  class MessagesToken{
    - List<Mail> mailList 
    + MessagesToken(List<Mail> mailList)
    + getMailList(): List<Mail>
    + setMailList(List<Mail> mailList): void
  }

  class ErrorToken {
    - String message
    - Error error

    + ErrorToken(Error error, String message)
    + getError(): Error
    + getMessage(): String
    + setError(Error error): void
    + setMessage(String message): void
    + toString(): String
  }

  abstract class RequestToken{
    + RequestToken()
  }

  abstract class ProtocolToken{
    + ProtocolToken()
  }

  AckToken --|> ResponseToken
  MessagesToken --|> ResponseToken
  MessagesToken -->"0..N" "data::Mail":-mailList
  ErrorToken --|> ResponseToken
  ErrorToken --> "data::Error": - error
  ProtocolToken --> "java::io::Serializable": implements
  RequestToken --|> ProtocolToken
  ResponseToken --|> ProtocolToken
  SynchToken --|> RequestToken
  InitToken --|> RequestToken
  InitToken --> "data::User": - u
  SendMessageToken --|> RequestToken
  SendMessageToken --> "data::Mail":- mail
  RemoveMessageToken --|> RequestToken
  ProtocolException --> ErrorToken: -errorToken
  ProtocolClient -up-|> ProtocolHandler
  ProtocolServer --|> ProtocolHandler
  ProtocolServer --> "data::Model":-model
  ProtocolServer --> "data::User": - clientUser
  ProtocolServer --> "data::Logger": - logger
}

newpage
hide empty members
namespace view {

  class ClientApp {
    - Stage primaryStage
    - ClientController clientController
    + start(Stage primaryStage): void
    + stop(): void
    + {static} main(String[] args): void
  }

  class ServerApp {
    - Stage primaryStage
    - ServerController serverController
    + start(Stage primaryStage) throws Exception: void
    + stop(): void
    + {static} main(String[] args): void
  }

  ServerApp --> "controller::ServerController":- serverController
  ClientApp --> "controller::ClientController":- clientController
}

namespace controller {

  class ServerController {
    - ServerManager serverManager
    - Logger logger
    - BorderPane mainPane
    - Menu configMenu
    - TextArea logArea
    - Label serverStatus
    + initialize(): void
    + onServerStart(): void
    + onServerStop(): void
    + onOpenConfig(): void
    + onResetConfig(): void
    + onAbout(): void
  }


  class MailFormController {
    - ClientManager clientManager
    - Mail openedMail
    - boolean onReply = false
    - TextField destField
    - TextField subjectField
    - TextArea messageArea
    - Button cancelButton
    - Button sendButton
    - Button replyButton
    - Button replyAllButton
    - Button forwardButton
    - Label destLabel
    + initialize(): void
    + setClientManager(ClientManager clientManager): void
    + openMail(Mail mail): void
    + newMail(): void
    + onCancel(): void
    + onSend(): void
    + onReply(): void
    + onReplyAll(): void
    + onForward(): void
    - exit(): void
  }

  class ConfigController {
    - DataModel model
    - Config config
    - Logger logger
    - Button saveButton
    - TextField address
    - TextField port
    - TextField logPath
    - ChoiceBox<LogLevel> logLevelChoiceBox
    + initialize(): void
    + onExit(): void
    + onSave(): void
    + setModel(DataModel model): void
    - allSetted(): boolean
  }

  class ClientController {
    + final {static} int pollingRate = 1000
    - volatile boolean running
    - volatile boolean disableNextAlert = true
    - ClientManager clientManager
    - Thread mailUpdater
    - Mail selectedMail
    - TableView<Mail> mailListView
    - TableColumn<Mail, String> senderCol
    - TableColumn<Mail, String> subjectCol
    - TableColumn<Mail, Date> dateCol
    - Button removeButton
    - Button openButton
    - Button sendButton
    + void initialize(): void
    + void onSend(): void
    + void onOpen(): void
    + void onRemove(): void
    + void onClose(): void
    + void onChangeUser(): void
    + void onAbout(): void
    - void resetInbox(): void
    - void changeAddress(String address): void
    - void setTitle(String title): void
  }


  ServerController --> "data::Logger":- logger
  ServerController --> "server::ServerManager":- serverManager
  MailFormController --> "client::ClientManager":- clientManager
  MailFormController --> "data::Mail":- openedMail
  ConfigController --> "data::DataModel":- model
  ConfigController --> "data::Config":- config
  ConfigController --> "data::Logger":- logger
  ClientController --> "client::ClientManager":- clientManager
  ClientController --> "data::Mail":- selectedMail 
}
@enduml
