mirror of
https://github.com/Akomry/sae302_applicom.git
synced 2025-12-06 08:43:54 +00:00
feat(contacts): ajout de la classe Contact.java, ContactMap.java, gestion des objets Contact dans l'interface graphique
This commit is contained in:
parent
45f18e9309
commit
e9480590a7
14 changed files with 649 additions and 36 deletions
16
chat/pom.xml
16
chat/pom.xml
|
|
@ -25,6 +25,22 @@
|
||||||
<artifactId>javafx-fxml</artifactId>
|
<artifactId>javafx-fxml</artifactId>
|
||||||
<version>18</version>
|
<version>18</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjfx</groupId>
|
||||||
|
<artifactId>javafx-swing</artifactId>
|
||||||
|
<version>19</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter-params</artifactId>
|
||||||
|
<version>5.8.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.synedra</groupId>
|
||||||
|
<artifactId>validatorfx</artifactId>
|
||||||
|
<version>0.5.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,12 @@ module rtgre.chat {
|
||||||
requires javafx.fxml;
|
requires javafx.fxml;
|
||||||
requires java.logging;
|
requires java.logging;
|
||||||
requires java.desktop;
|
requires java.desktop;
|
||||||
|
requires javafx.swing;
|
||||||
|
requires net.synedra.validatorfx;
|
||||||
|
|
||||||
|
|
||||||
opens rtgre.chat to javafx.fxml;
|
opens rtgre.chat to javafx.fxml;
|
||||||
exports rtgre.chat;
|
exports rtgre.chat;
|
||||||
|
exports rtgre.chat.graphisme;
|
||||||
|
opens rtgre.chat.graphisme to javafx.fxml;
|
||||||
}
|
}
|
||||||
|
|
@ -15,8 +15,8 @@ import java.util.logging.Logger;
|
||||||
|
|
||||||
public class ChatApplication extends Application {
|
public class ChatApplication extends Application {
|
||||||
public static final Logger LOGGER = Logger.getLogger(ChatApplication.class.getCanonicalName());
|
public static final Logger LOGGER = Logger.getLogger(ChatApplication.class.getCanonicalName());
|
||||||
|
|
||||||
public class EssaiLogger {
|
public class EssaiLogger {
|
||||||
/* . . . */
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
InputStream is = EssaiLogger.class.getClassLoader()
|
InputStream is = EssaiLogger.class.getClassLoader()
|
||||||
|
|
@ -26,8 +26,8 @@ public class ChatApplication extends Application {
|
||||||
LOGGER.log(Level.INFO, "Cannot read configuration file", e);
|
LOGGER.log(Level.INFO, "Cannot read configuration file", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* . . . */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Stage stage) throws IOException {
|
public void start(Stage stage) throws IOException {
|
||||||
FXMLLoader fxmlLoader = new FXMLLoader(ChatApplication.class.getResource("chat-view.fxml"));
|
FXMLLoader fxmlLoader = new FXMLLoader(ChatApplication.class.getResource("chat-view.fxml"));
|
||||||
|
|
|
||||||
|
|
@ -2,22 +2,47 @@ package rtgre.chat;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.beans.Observable;
|
import javafx.beans.Observable;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
import javafx.event.Event;
|
import javafx.event.Event;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
import javafx.scene.control.*;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.image.ImageView;
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.input.MouseEvent;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.VBox;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import net.synedra.validatorfx.Check;
|
||||||
|
import net.synedra.validatorfx.TooltipWrapper;
|
||||||
|
import net.synedra.validatorfx.Validator;
|
||||||
|
import rtgre.chat.graphisme.ContactListViewCell;
|
||||||
|
import rtgre.modeles.Contact;
|
||||||
|
import rtgre.modeles.ContactMap;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static rtgre.chat.ChatApplication.LOGGER;
|
import static rtgre.chat.ChatApplication.LOGGER;
|
||||||
|
|
||||||
public class ChatController implements Initializable {
|
public class ChatController implements Initializable {
|
||||||
|
|
||||||
|
private static final Pattern LOGIN_PATTERN = Pattern.compile("^([a-z][a-z0-9]{2,7})$");
|
||||||
|
private static final Pattern HOST_PATTERN = Pattern.compile("/^[a-z]*((\\:?)\\d{1,5})?$/gm");
|
||||||
public MenuItem hostAddMenuItem;
|
public MenuItem hostAddMenuItem;
|
||||||
public MenuItem avatarMenuItem;
|
public MenuItem avatarMenuItem;
|
||||||
public MenuItem aboutMenuItem;
|
public MenuItem aboutMenuItem;
|
||||||
|
|
@ -28,11 +53,15 @@ public class ChatController implements Initializable {
|
||||||
public SplitPane exchangeSplitPane;
|
public SplitPane exchangeSplitPane;
|
||||||
public ListView postListView;
|
public ListView postListView;
|
||||||
public ListView roomsListView;
|
public ListView roomsListView;
|
||||||
public ListView contactListView;
|
public ListView contactsListView;
|
||||||
public TextField messageTextField;
|
public TextField messageTextField;
|
||||||
public Button sendButton;
|
public Button sendButton;
|
||||||
public Label statusLabel;
|
public Label statusLabel;
|
||||||
public Label dateTimeLabel;
|
public Label dateTimeLabel;
|
||||||
|
public Contact contact;
|
||||||
|
private ContactMap contactMap = new ContactMap();
|
||||||
|
private ObservableList<Contact> contactObservableList = FXCollections.observableArrayList();
|
||||||
|
Validator validatorLogin = new Validator();
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -53,13 +82,74 @@ public class ChatController implements Initializable {
|
||||||
|
|
||||||
statusLabel.setText("not connected to " + hostComboBox.getValue());
|
statusLabel.setText("not connected to " + hostComboBox.getValue());
|
||||||
|
|
||||||
|
connectionButton.disableProperty().bind(validatorLogin.containsErrorsProperty());
|
||||||
|
connectionButton.selectedProperty().addListener(this::handleConnection);
|
||||||
|
loginTextField.disableProperty().bind(connectionButton.selectedProperty());
|
||||||
|
hostComboBox.disableProperty().bind(connectionButton.selectedProperty());
|
||||||
|
|
||||||
|
avatarMenuItem.setOnAction(this::handleAvatarChange);
|
||||||
|
avatarImageView.setOnMouseClicked(this::handleAvatarChange);
|
||||||
|
|
||||||
|
initContactListView();
|
||||||
|
|
||||||
|
validatorLogin.createCheck()
|
||||||
|
.dependsOn("login", loginTextField.textProperty())
|
||||||
|
.withMethod(this::checkLogin)
|
||||||
|
.decorates(loginTextField)
|
||||||
|
.immediate();
|
||||||
|
|
||||||
|
|
||||||
|
/* /!\ Set-up d'environnement de test /!\ */
|
||||||
|
/* -------------------------------------- */
|
||||||
|
loginTextField.setText("riri");
|
||||||
|
connectionButton.setSelected(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleAvatarChange(Event event) {
|
||||||
|
/**
|
||||||
|
* Ouvre une fenêtre de dialogue permettant de choisir son avatar
|
||||||
|
*/
|
||||||
|
FileChooser fileChooser = new FileChooser();
|
||||||
|
Stage stage = (Stage) avatarImageView.getScene().getWindow();
|
||||||
|
fileChooser.setTitle("Select Avatar");
|
||||||
|
fileChooser.getExtensionFilters().addAll(
|
||||||
|
new FileChooser.ExtensionFilter("Image Files", "*.png", "*.jpg")
|
||||||
|
);
|
||||||
|
File selectedFile = fileChooser.showOpenDialog(stage);
|
||||||
|
if (selectedFile != null) {
|
||||||
|
avatarImageView.setImage(new Image(selectedFile.toURI().toString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void handleConnection(Observable observable) {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
if (connectionButton.isSelected()) {
|
||||||
|
java.awt.Image img = SwingFXUtils.fromFXImage(this.avatarImageView.getImage(), null);
|
||||||
|
this.contact = new Contact(loginTextField.getText(), img);
|
||||||
|
contactMap.put(this.contact.getLogin(), this.contact);
|
||||||
|
System.out.println("Nouveau contact : " + contact);
|
||||||
|
System.out.println(contactMap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkLogin(Check.Context context) {
|
||||||
|
String login = context.get("login");
|
||||||
|
if (!LOGIN_PATTERN.matcher(login).matches()) {
|
||||||
|
context.error("Format de login non respecté");
|
||||||
|
}
|
||||||
|
if (login.equals("system")) {
|
||||||
|
context.error("Le login ne peut pas être system");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void statusNameUpdate(Event event) {
|
private void statusNameUpdate(Event event) {
|
||||||
statusLabel.setText("not connected to " + hostComboBox.getValue());
|
statusLabel.setText("not connected to " + hostComboBox.getValue());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -75,4 +165,20 @@ public class ChatController implements Initializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initContactListView() {
|
||||||
|
try {
|
||||||
|
contactsListView.setCellFactory(contactListView -> new ContactListViewCell());
|
||||||
|
contactsListView.setItems(contactObservableList);
|
||||||
|
File avatars = new File(getClass().getResource("avatars.png").toURI());
|
||||||
|
Contact riri = new Contact("riri", false, avatars);
|
||||||
|
Contact fifi = new Contact("fifi", true, avatars);
|
||||||
|
contactObservableList.add(riri);
|
||||||
|
contactMap.add(riri);
|
||||||
|
contactObservableList.add(fifi);
|
||||||
|
contactMap.add(fifi);
|
||||||
|
} catch (Exception e) {
|
||||||
|
LOGGER.severe(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,88 @@
|
||||||
|
package rtgre.chat.graphisme;
|
||||||
|
|
||||||
|
import rtgre.chat.ChatController;
|
||||||
|
import javafx.embed.swing.SwingFXUtils;
|
||||||
|
import javafx.geometry.Pos;
|
||||||
|
import javafx.scene.control.ListCell;
|
||||||
|
import javafx.scene.image.Image;
|
||||||
|
import javafx.scene.image.ImageView;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Priority;
|
||||||
|
import javafx.scene.paint.Color;
|
||||||
|
import javafx.scene.shape.Circle;
|
||||||
|
import javafx.scene.text.Font;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import rtgre.modeles.Contact;
|
||||||
|
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
|
||||||
|
import static rtgre.chat.ChatApplication.LOGGER;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Classe modélisant la fabrique de cellule de la vue des contacts
|
||||||
|
* visibles/connectés {@link ChatController#contactsListView}.
|
||||||
|
*
|
||||||
|
* @see ListCell
|
||||||
|
*/
|
||||||
|
public class ContactListViewCell extends ListCell<Contact> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback déclenchée à chaque modification d'un objet d'une liste d'observable.
|
||||||
|
*
|
||||||
|
* @param contact Le contact à mettre à jour
|
||||||
|
* @param empty La liste de cellule doit-elle être complètement remise à zéro ?
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void updateItem(Contact contact, boolean empty) {
|
||||||
|
super.updateItem(contact, empty);
|
||||||
|
if (empty) {
|
||||||
|
setGraphic(null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Cas d'un contact
|
||||||
|
updateContact(contact);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mise à jour de la cellule d'un contact.
|
||||||
|
*
|
||||||
|
* @param contact Le contact à mettre à jour
|
||||||
|
*/
|
||||||
|
|
||||||
|
private void updateContact(Contact contact) {
|
||||||
|
LOGGER.finest("Mise à jour de " + contact);
|
||||||
|
|
||||||
|
String unreadCountNotif = (contact.getUnreadCount() == 0) ? "" : " (%d)".formatted(contact.getUnreadCount());
|
||||||
|
LOGGER.finest("unread: %s %s".formatted(contact.getLogin(), unreadCountNotif));
|
||||||
|
Text loginText = new Text(contact.getLogin() + unreadCountNotif);
|
||||||
|
loginText.setFont(Font.font(null, 12)); // FontWeight.BOLD, 14));
|
||||||
|
loginText.setFill(contact.isConnected() ? Color.BLACK : Color.GRAY);
|
||||||
|
|
||||||
|
Circle circle = new Circle(5, 5, 5);
|
||||||
|
circle.setFill(contact.isConnected() ? Color.CADETBLUE : Color.FIREBRICK);
|
||||||
|
// circle.setOpacity(contact.is_connected() ? 1 : 0.5);
|
||||||
|
|
||||||
|
Image avatar;
|
||||||
|
ImageView view = new ImageView();
|
||||||
|
|
||||||
|
if (contact.getAvatar() != null) {
|
||||||
|
avatar = SwingFXUtils.toFXImage((BufferedImage) contact.getAvatar(), null);
|
||||||
|
view = new ImageView(avatar);
|
||||||
|
}
|
||||||
|
view.setOpacity(contact.isConnected() ? 1 : 0.5);
|
||||||
|
view.setFitWidth(15);
|
||||||
|
view.setFitHeight(15);
|
||||||
|
|
||||||
|
HBox temp = new HBox(circle);
|
||||||
|
temp.setAlignment(Pos.CENTER_RIGHT);
|
||||||
|
HBox.setHgrow(temp, Priority.ALWAYS);
|
||||||
|
HBox hBox = new HBox(view, loginText, temp);
|
||||||
|
hBox.setSpacing(5.0);
|
||||||
|
hBox.setAlignment(Pos.CENTER_LEFT);
|
||||||
|
|
||||||
|
setGraphic(hBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,18 +1,65 @@
|
||||||
package rtgre.modeles;
|
package rtgre.modeles;
|
||||||
|
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class Contact {
|
public class Contact {
|
||||||
protected String login;
|
protected String login;
|
||||||
protected java.awt.Image avatar;
|
protected java.awt.Image avatar;
|
||||||
protected boolean connected;
|
protected boolean connected;
|
||||||
protected String currentRoom;
|
protected String currentRoom;
|
||||||
|
|
||||||
Contact(String login, java.awt.Image avatar) {
|
public Contact(String login, java.awt.Image avatar) {
|
||||||
|
/**
|
||||||
|
* Crée un objet Contact
|
||||||
|
* @param: String login
|
||||||
|
* @param: java.awt.Image avatar
|
||||||
|
*/
|
||||||
this.login = login;
|
this.login = login;
|
||||||
this.avatar = avatar;
|
this.avatar = avatar;
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
this.currentRoom = null;
|
this.currentRoom = null;
|
||||||
}
|
}
|
||||||
|
public Contact(String login, boolean connected, java.awt.Image avatar) {
|
||||||
|
/**
|
||||||
|
* Crée un objet Contact
|
||||||
|
* @param: String login
|
||||||
|
* @param: boolean connected
|
||||||
|
* @param: java.awt.Image avatar
|
||||||
|
*/
|
||||||
|
this.login = login;
|
||||||
|
this.avatar = avatar;
|
||||||
|
this.connected = connected;
|
||||||
|
this.currentRoom = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCurrentRoom() {
|
||||||
|
return currentRoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Contact(String login, boolean connected, File banques_avatars) {
|
||||||
|
/**
|
||||||
|
* Crée un objet Contact
|
||||||
|
* @param: String login
|
||||||
|
* @param: boolean connected
|
||||||
|
* @param: File banques_avatars
|
||||||
|
*/
|
||||||
|
this.login = login;
|
||||||
|
try {
|
||||||
|
this.avatar = avatarFromLogin(banques_avatars, login);
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("Impossible de créer l'utilisateur " + login);
|
||||||
|
System.out.println(e.getMessage());
|
||||||
|
System.out.println(banques_avatars);
|
||||||
|
}
|
||||||
|
this.connected = connected;
|
||||||
|
this.currentRoom = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getLogin() {
|
public String getLogin() {
|
||||||
return this.login;
|
return this.login;
|
||||||
|
|
@ -24,20 +71,52 @@ public class Contact {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "";
|
return "@" + this.login;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnected() {
|
||||||
|
return this.connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setConnected(boolean connected) {
|
public void setConnected(boolean connected) {
|
||||||
this.connected = connected;
|
this.connected = connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean equals(Object o) {return true;}
|
public boolean equals(Object o) {
|
||||||
/*if (this.login == o.login) {
|
if (this == o) return true;
|
||||||
return true;
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
} else {
|
Contact contact = (Contact) o;
|
||||||
return false;
|
return Objects.equals(login, contact.login);
|
||||||
}
|
}
|
||||||
}*/
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hashCode(login);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getUnreadCount() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BufferedImage avatarFromLogin(File fichier, String login) throws IOException {
|
||||||
|
/**
|
||||||
|
* Renvoie une sous-image en fonction d'une banque d'image et d'un login.
|
||||||
|
* @param: File fichier
|
||||||
|
* @param: String login
|
||||||
|
*/
|
||||||
|
BufferedImage img = ImageIO.read(fichier);
|
||||||
|
int width = img.getWidth() / 9;
|
||||||
|
int height = img.getHeight();
|
||||||
|
int n = Integer.remainderUnsigned(login.hashCode(), 9);
|
||||||
|
return img.getSubimage(n*width, 0, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAvatarFromFile(File f) {
|
||||||
|
try {
|
||||||
|
this.avatar = avatarFromLogin(f, this.login);
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("Erreur : " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
chat/src/main/java/rtgre/modeles/ContactMap.java
Normal file
12
chat/src/main/java/rtgre/modeles/ContactMap.java
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
package rtgre.modeles;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
public class ContactMap extends TreeMap<String, Contact> {
|
||||||
|
public void add(Contact contact) {
|
||||||
|
this.put(contact.login, contact);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Contact getContact(String login) {
|
||||||
|
return this.get(login);
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
chat/src/main/resources/rtgre/chat/avatar1.png
Normal file
BIN
chat/src/main/resources/rtgre/chat/avatar1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
BIN
chat/src/main/resources/rtgre/chat/avatar2.png
Normal file
BIN
chat/src/main/resources/rtgre/chat/avatar2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
chat/src/main/resources/rtgre/chat/avatars.png
Normal file
BIN
chat/src/main/resources/rtgre/chat/avatars.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
|
|
@ -1,27 +1,13 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
<?import javafx.geometry.Insets?>
|
<?import javafx.geometry.*?>
|
||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.*?>
|
||||||
<?import javafx.scene.control.ComboBox?>
|
<?import javafx.scene.image.*?>
|
||||||
<?import javafx.scene.control.Label?>
|
<?import javafx.scene.layout.*?>
|
||||||
<?import javafx.scene.control.ListView?>
|
|
||||||
<?import javafx.scene.control.Menu?>
|
|
||||||
<?import javafx.scene.control.MenuBar?>
|
|
||||||
<?import javafx.scene.control.MenuItem?>
|
|
||||||
<?import javafx.scene.control.Separator?>
|
|
||||||
<?import javafx.scene.control.SplitPane?>
|
|
||||||
<?import javafx.scene.control.TextField?>
|
|
||||||
<?import javafx.scene.control.ToggleButton?>
|
|
||||||
<?import javafx.scene.image.ImageView?>
|
|
||||||
<?import javafx.scene.layout.ColumnConstraints?>
|
|
||||||
<?import javafx.scene.layout.GridPane?>
|
|
||||||
<?import javafx.scene.layout.HBox?>
|
|
||||||
<?import javafx.scene.layout.RowConstraints?>
|
|
||||||
<?import javafx.scene.layout.VBox?>
|
|
||||||
|
|
||||||
<VBox alignment="CENTER" minHeight="400.0" minWidth="600.0" prefHeight="500.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/18" xmlns:fx="http://javafx.com/fxml/1" fx:controller="rtgre.chat.ChatController">
|
<VBox alignment="CENTER" minHeight="400.0" minWidth="600.0" prefHeight="500.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="rtgre.chat.ChatController">
|
||||||
<children>
|
<children>
|
||||||
<MenuBar>
|
<MenuBar VBox.vgrow="NEVER">
|
||||||
<menus>
|
<menus>
|
||||||
<Menu mnemonicParsing="false" text="Edit">
|
<Menu mnemonicParsing="false" text="Edit">
|
||||||
<items>
|
<items>
|
||||||
|
|
@ -77,7 +63,7 @@
|
||||||
<SplitPane dividerPositions="0.1" orientation="VERTICAL" prefHeight="200.0" prefWidth="160.0">
|
<SplitPane dividerPositions="0.1" orientation="VERTICAL" prefHeight="200.0" prefWidth="160.0">
|
||||||
<items>
|
<items>
|
||||||
<ListView fx:id="roomsListView" prefHeight="200.0" prefWidth="200.0" />
|
<ListView fx:id="roomsListView" prefHeight="200.0" prefWidth="200.0" />
|
||||||
<ListView fx:id="contactListView" prefHeight="200.0" prefWidth="200.0" />
|
<ListView fx:id="contactsListView" prefHeight="200.0" prefWidth="200.0" />
|
||||||
</items>
|
</items>
|
||||||
</SplitPane>
|
</SplitPane>
|
||||||
</items>
|
</items>
|
||||||
|
|
@ -111,7 +97,7 @@
|
||||||
</children>
|
</children>
|
||||||
</HBox>
|
</HBox>
|
||||||
<Separator prefWidth="200.0" />
|
<Separator prefWidth="200.0" />
|
||||||
<HBox>
|
<HBox VBox.vgrow="NEVER">
|
||||||
<children>
|
<children>
|
||||||
<Label text="Status : " />
|
<Label text="Status : " />
|
||||||
<Label fx:id="statusLabel" text="Not connected" />
|
<Label fx:id="statusLabel" text="Not connected" />
|
||||||
|
|
|
||||||
155
chat/src/test/java/rtgre/modeles/ContactBaseTest1.java
Normal file
155
chat/src/test/java/rtgre/modeles/ContactBaseTest1.java
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
package rtgre.modeles;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.*;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
|
import java.lang.reflect.*;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||||
|
|
||||||
|
/** Tests unitaires du modèle de base de Contact (étape 1) */
|
||||||
|
|
||||||
|
class ContactBaseTest1 {
|
||||||
|
|
||||||
|
static Class<?> classe = Contact.class;
|
||||||
|
static String module = "rtgre.modeles";
|
||||||
|
|
||||||
|
@DisplayName("01-Structure de la classe Contact")
|
||||||
|
@Nested
|
||||||
|
class StructureTest {
|
||||||
|
|
||||||
|
static List<String> constructeursSignatures;
|
||||||
|
static List<String> methodesSignatures;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void init() {
|
||||||
|
Constructor<?>[] constructeurs = classe.getConstructors();
|
||||||
|
constructeursSignatures = Arrays.stream(constructeurs).map(Constructor::toString).collect(Collectors.toList());
|
||||||
|
Method[] methodes = classe.getDeclaredMethods();
|
||||||
|
methodesSignatures = Arrays.stream(methodes).map(Method::toString).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attributs
|
||||||
|
*/
|
||||||
|
static Stream<Arguments> attributsProvider() {
|
||||||
|
return Stream.of(
|
||||||
|
arguments("login", "java.lang.String", Modifier.PROTECTED),
|
||||||
|
arguments("avatar", "java.awt.Image", Modifier.PROTECTED),
|
||||||
|
arguments("connected", "boolean", Modifier.PROTECTED),
|
||||||
|
arguments("currentRoom", "java.lang.String", Modifier.PROTECTED)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("Déclaration des attributs : nom, type et visibilité")
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("attributsProvider")
|
||||||
|
void testDeclarationAttributs(String nom, String type, int modifier) throws NoSuchFieldException {
|
||||||
|
Field field = classe.getDeclaredField(nom);
|
||||||
|
Assertions.assertEquals(type, field.getType().getName(),
|
||||||
|
"Type " + nom + " erroné : doit être " + type);
|
||||||
|
Assertions.assertEquals(modifier, field.getModifiers(),
|
||||||
|
"Visibilité " + nom + " erronée : doit être " + modifier);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructeurs
|
||||||
|
*/
|
||||||
|
static Stream<Arguments> constructeursProvider() {
|
||||||
|
return Stream.of(
|
||||||
|
arguments("public %s.Contact(java.lang.String,java.awt.Image)")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("Déclaration des constructeurs (base)")
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("constructeursProvider")
|
||||||
|
void testConstructeurs1(String signature) {
|
||||||
|
Assertions.assertTrue(constructeursSignatures.contains(String.format(signature, module)),
|
||||||
|
String.format("Constructeur non déclaré : doit être %s\nalors que sont déclarés %s",
|
||||||
|
signature, constructeursSignatures));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Méthodes
|
||||||
|
*/
|
||||||
|
static Stream<Arguments> methodesProvider1() {
|
||||||
|
return Stream.of(
|
||||||
|
arguments("getLogin", "public java.lang.String %s.Contact.getLogin()"),
|
||||||
|
arguments("getAvatar", "public java.awt.Image %s.Contact.getAvatar()"),
|
||||||
|
arguments("isConnected", "public boolean %s.Contact.isConnected()"),
|
||||||
|
arguments("toString", "public boolean %s.Contact.isConnected()"),
|
||||||
|
arguments("setConnected", "public java.lang.String %s.Contact.toString()"),
|
||||||
|
arguments("equals", "public boolean %s.Contact.equals(java.lang.Object)")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("Déclaration des méthodes (base)")
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("methodesProvider1")
|
||||||
|
void testDeclarationMethodes1(String nom, String signature) {
|
||||||
|
Assertions.assertTrue(methodesSignatures.contains(String.format(signature, module, module)),
|
||||||
|
String.format("Méthode non déclarée : doit être %s\nalors que sont déclarés %s",
|
||||||
|
signature, methodesSignatures));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@DisplayName("02-Instanciation et getters")
|
||||||
|
@Nested
|
||||||
|
class InstanciationContactTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("sans avatar : getters de login, connected")
|
||||||
|
void TestConstructeurParDefautSansAvatar() {
|
||||||
|
Contact riri = new Contact("riri", null);
|
||||||
|
Assertions.assertEquals("riri", riri.getLogin(), "Login erroné");
|
||||||
|
Assertions.assertFalse(riri.isConnected(), "Etat par défaut erroné");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("03-Modification")
|
||||||
|
@Nested
|
||||||
|
class ModificationContactTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Setter de connexion")
|
||||||
|
void TestEtatConnexion() {
|
||||||
|
Contact riri = new Contact("riri", null);
|
||||||
|
riri.setConnected(true);
|
||||||
|
Assertions.assertTrue(riri.isConnected(), "Changement d'état erroné");
|
||||||
|
riri.setConnected(false);
|
||||||
|
Assertions.assertFalse(riri.isConnected(), "Changement d'état erroné");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("04-Représentation textuelle")
|
||||||
|
void TestToString() {
|
||||||
|
Contact riri = new Contact("riri", null);
|
||||||
|
Assertions.assertEquals("@riri", riri.toString(),
|
||||||
|
"Représentation textuelle erronée");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("05-Egalité")
|
||||||
|
void TestEquals() {
|
||||||
|
Contact riri = new Contact("riri", null);
|
||||||
|
Contact riri2 = new Contact("riri", null);
|
||||||
|
Contact fifi = new Contact("fifi", null);
|
||||||
|
Assertions.assertEquals(riri, riri2, "Comparaison erronée");
|
||||||
|
Assertions.assertNotEquals(riri, fifi, "Comparaison erronée");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
47
chat/src/test/java/rtgre/modeles/ContactMapTest.java
Normal file
47
chat/src/test/java/rtgre/modeles/ContactMapTest.java
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
package rtgre.modeles;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Nested;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.TreeMap;
|
||||||
|
|
||||||
|
class ContactMapTest {
|
||||||
|
|
||||||
|
static Class<?> classe = ContactMap.class;
|
||||||
|
|
||||||
|
@DisplayName("01-Structure")
|
||||||
|
@Nested
|
||||||
|
class StructureContactMapTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Heritage")
|
||||||
|
void testHeritage() {
|
||||||
|
ContactMap contactMap = new ContactMap();
|
||||||
|
Assertions.assertInstanceOf(TreeMap.class, contactMap, "doit hériter de TreeMap");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("02-Ajout")
|
||||||
|
@Nested
|
||||||
|
class AddTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Ajout d'un contact")
|
||||||
|
void TestAdd() {
|
||||||
|
Contact riri = new Contact("riri", null);
|
||||||
|
ContactMap contactMap = new ContactMap();
|
||||||
|
contactMap.add(riri);
|
||||||
|
|
||||||
|
Assertions.assertTrue(contactMap.containsKey("riri"),
|
||||||
|
"Les clés sont les logins");
|
||||||
|
Assertions.assertTrue(contactMap.containsValue(riri),
|
||||||
|
"Les valeurs sont les contacts");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
120
chat/src/test/java/rtgre/modeles/ContactWithAvatarTest2.java
Normal file
120
chat/src/test/java/rtgre/modeles/ContactWithAvatarTest2.java
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
package rtgre.modeles;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.*;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.params.provider.Arguments.arguments;
|
||||||
|
|
||||||
|
/** Tests unitaires du modèle de base de Contact (étape 1) */
|
||||||
|
|
||||||
|
class ContactWithAvatarTest2 {
|
||||||
|
|
||||||
|
static Class<?> classe = Contact.class;
|
||||||
|
static String module = "rtgre.modeles";
|
||||||
|
|
||||||
|
@DisplayName("01-Structure de la classe Contact")
|
||||||
|
@Nested
|
||||||
|
class StructureTest {
|
||||||
|
|
||||||
|
static List<String> constructeursSignatures;
|
||||||
|
static List<String> methodesSignatures;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void init() {
|
||||||
|
Constructor<?>[] constructeurs = classe.getConstructors();
|
||||||
|
constructeursSignatures = Arrays.stream(constructeurs).map(Constructor::toString).collect(Collectors.toList());
|
||||||
|
Method[] methodes = classe.getDeclaredMethods();
|
||||||
|
methodesSignatures = Arrays.stream(methodes).map(Method::toString).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static Stream<Arguments> constructeursProvider2() {
|
||||||
|
return Stream.of(
|
||||||
|
arguments("public %s.Contact(java.lang.String,boolean,java.io.File)")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// @Disabled("Jusqu'à ce que soit codé les avatars à partir d'un fichier")
|
||||||
|
@DisplayName("Déclaration des constructeurs (avec avatars à partir d'un fichier)")
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("constructeursProvider2")
|
||||||
|
void testConstructeurs2(String signature) {
|
||||||
|
Assertions.assertTrue(constructeursSignatures.contains(String.format(signature, module)),
|
||||||
|
String.format("Constructeur non déclaré : doit être %s\nalors que sont déclarés %s",
|
||||||
|
signature, constructeursSignatures));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static Stream<Arguments> methodesProvider2() {
|
||||||
|
return Stream.of(
|
||||||
|
arguments("avatarFromLogin", "public static java.awt.image.BufferedImage %s.Contact.avatarFromLogin(java.io.File,java.lang.String) throws java.io.IOException"),
|
||||||
|
arguments("setAvatarFromFile", "public void %s.Contact.setAvatarFromFile(java.io.File)")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// @Disabled("Jusqu'à ce que soit codé les avatars à partir d'un fichier")
|
||||||
|
@DisplayName("Déclaration des méthodes (avec avatars à partir d'un fichier)")
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("methodesProvider2")
|
||||||
|
void testDeclarationMethodes2(String nom, String signature) {
|
||||||
|
Assertions.assertTrue(methodesSignatures.contains(String.format(signature, module, module)),
|
||||||
|
String.format("Méthode non déclarée : doit être %s\nalors que sont déclarés %s",
|
||||||
|
signature, methodesSignatures));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@DisplayName("02-Instanciation et getters")
|
||||||
|
@Nested
|
||||||
|
class InstanciationContactTest {
|
||||||
|
|
||||||
|
// @Disabled("Jusqu'à ce que soit codé les avatars à partir d'un fichier")
|
||||||
|
@Test
|
||||||
|
@DisplayName("avec avatar : getters de avatar")
|
||||||
|
void TestConstructeurParDefautAvecAvatar() throws IOException {
|
||||||
|
String work_dir = System.getProperty("user.dir");
|
||||||
|
Assertions.assertTrue(work_dir.endsWith("chat"),
|
||||||
|
"Le working dir doit être <projet>/chat/ et non : " + work_dir);
|
||||||
|
File f = new File("src/main/resources/rtgre/chat/anonymous.png");
|
||||||
|
Assertions.assertTrue(f.canRead(), "Fichier manquant " + f.getAbsolutePath());
|
||||||
|
Image avatar = ImageIO.read(f);
|
||||||
|
Contact fifi = new Contact("fifi", avatar);
|
||||||
|
Assertions.assertEquals(avatar, fifi.getAvatar(), "Avatar erroné");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// @Disabled("Jusqu'à ce que soit codé les avatars à partir d'un fichier")
|
||||||
|
@Nested
|
||||||
|
@DisplayName("06-Avatar à partir d'un fichier")
|
||||||
|
class AvatarFromFilesTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("A partir d'un fichier")
|
||||||
|
void TestSetFromFile() throws IOException {
|
||||||
|
String work_dir = System.getProperty("user.dir");
|
||||||
|
Assertions.assertTrue(work_dir.endsWith("chat"),
|
||||||
|
"Le working dir doit être <projet>/chat/ et non : " + work_dir);
|
||||||
|
File f = new File("src/main/resources/rtgre/chat/avatar1.png");
|
||||||
|
Assertions.assertTrue(f.canRead(), "Fichier manquant " + f.getAbsolutePath());
|
||||||
|
Contact fifi = new Contact("fifi", null);
|
||||||
|
fifi.setAvatarFromFile(f);
|
||||||
|
Assertions.assertNotNull(fifi.getAvatar(), "Avatar non chargé");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue