diff --git a/resources/log4j2.xml b/resources/log4j2.xml new file mode 100644 index 0000000..ea50218 --- /dev/null +++ b/resources/log4j2.xml @@ -0,0 +1,23 @@ + + + + logs + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/logging_testing_6/ChatServer.java b/src/logging_testing_6/ChatServer.java new file mode 100644 index 0000000..c1d203b --- /dev/null +++ b/src/logging_testing_6/ChatServer.java @@ -0,0 +1,130 @@ +package logging_testing_6; + + +import logging_testing_6.auth.AuthService; +import logging_testing_6.auth.AuthServiceImpl; +import logging_testing_6.gui.Message; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +//import org.slf4j.Logger; +//import org.slf4j.LoggerFactory; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ChatServer { + + // private static final String USER_CONNECTED_PATTERN = "/userconn"; +// private static final String USER_DISCONN_PATTERN = "/userdissconn"; + private static final Pattern AUTH_PATTERN = Pattern.compile("^/auth (\\w+) (\\w+)$"); + + private AuthService authService = new AuthServiceImpl(); + + private Map clientHandlerMap = Collections.synchronizedMap(new HashMap<>()); + private ExecutorService executorService; + + // private static final Logger logger = LoggerFactory.getLogger(ChatServer.class); + private static final Logger log = LogManager.getLogger(ChatServer.class); + + public static void main(String[] args) { + ChatServer chatServer = new ChatServer(); + chatServer.start(7777); + } + + public void start(int port) { + executorService = Executors.newCachedThreadPool(); + try (ServerSocket serverSocket = new ServerSocket(port)) { + log.info("Server started!"); + while (true) { + Socket socket = serverSocket.accept(); + DataInputStream inp = new DataInputStream(socket.getInputStream()); + DataOutputStream out = new DataOutputStream(socket.getOutputStream()); + log.info("New client connected!"); + + try { + String authMessage = inp.readUTF(); + Matcher matcher = AUTH_PATTERN.matcher(authMessage); + if (matcher.matches()) { + String username = matcher.group(1); + String password = matcher.group(2); + if (authService.authUser(username, password)) { + clientHandlerMap.put(username, new ClientHandler(username, socket, this)); + out.writeUTF("/auth successful"); + out.flush(); + broadcastUserConnected(); + + log.info("Authorization for user {} %s successful", username); + } else { + log.info("Authorization for user {} failed", username); + out.writeUTF("/auth fails"); + out.flush(); + socket.close(); + } + } else { + log.info("Incorrect authorization message {}", authMessage); + out.writeUTF("/auth fails"); + out.flush(); + socket.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } catch (IOException e) { + e.printStackTrace(); + } finally { +// authService.close(); + executorService.shutdown(); + } + } + + public void sendMessage(String userTo, String userFrom, String msg) throws IOException { + ClientHandler userToClientHandler = clientHandlerMap.get(userTo); + if (userToClientHandler != null) { + userToClientHandler.sendMessage(userFrom, msg); + } else { + log.debug("User {} not found. Message from {} is lost.", userTo, userFrom); + } + } + + public ExecutorService getExecutorService() { + return executorService; + } + + public void sendMessage(Message message) throws IOException { + String userTo = message.getUserTo(); + String userFrom = message.getUserFrom(); + String msg = message.getText(); + ClientHandler userToClientHandler = clientHandlerMap.get(userTo); + if (userToClientHandler != null) { + userToClientHandler.sendMessage(userFrom, msg); + } else { + log.debug("User {} not found. Message from {} is lost.", userTo, userFrom); + } + } + + public List getUserList() { + return new ArrayList<>(clientHandlerMap.keySet()); + } + + public void unsubscribeClient(ClientHandler clientHandler) { + clientHandlerMap.remove(clientHandler.getUsername()); + broadcastUserDisconnected(); + } + + public void broadcastUserConnected() { + // TODO сообщать о том, что конкретный пользователь подключился + } + + public void broadcastUserDisconnected() { + // TODO сообщать о том, что конкретный пользователь отключился + } +} diff --git a/src/logging_testing_6/ClientHandler.java b/src/logging_testing_6/ClientHandler.java new file mode 100644 index 0000000..7f466dc --- /dev/null +++ b/src/logging_testing_6/ClientHandler.java @@ -0,0 +1,77 @@ +package logging_testing_6; + +import logging_testing_6.gui.Message; +import logging_testing_6.history.HistoryKeeper; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.Socket; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +public class ClientHandler { + + private static final Pattern MESSAGE_PATTERN = Pattern.compile("^/w (\\w+) (.+)", Pattern.MULTILINE); + private static final String MESSAGE_SEND_PATTERN = "/w %s %s"; + private static final Logger log = LogManager.getLogger(ClientHandler.class); + + +// private final Thread handleThread; + private final DataInputStream inp; + private final DataOutputStream out; + private final ChatServer server; + private final String username; + private final Socket socket; + + public ClientHandler(String username, Socket socket, ChatServer server) throws IOException { + this.username = username; + this.socket = socket; + this.server = server; + this.inp = new DataInputStream(socket.getInputStream()); + this.out = new DataOutputStream(socket.getOutputStream()); + + server.getExecutorService().submit(() -> { + try { + while (!Thread.currentThread().isInterrupted()) { + String msg = inp.readUTF(); + log.info("Message from user {}: {}", username, msg); + + Matcher matcher = MESSAGE_PATTERN.matcher(msg); + if (matcher.matches()) { + String userTo = matcher.group(1); + String messageText = matcher.group(2); + Message message = new Message(username, userTo, messageText); + server.sendMessage(message); + + // Добавляем сообщения в файл истории отправителя + HistoryKeeper.saveMassageToHistoryFrom(message); + // Добавляем сообщения в файл истории получателя + HistoryKeeper.saveMassageToHistoryTo(message); + } + } + } catch (IOException e) { + e.printStackTrace(); + } finally { + log.info("Client {} disconnected", username); + try { + socket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + server.unsubscribeClient(ClientHandler.this); + } + }); + } + + public void sendMessage(String userTo, String msg) throws IOException { + out.writeUTF(String.format(MESSAGE_SEND_PATTERN, userTo, msg)); + } + + public String getUsername() { + return username; + } +} diff --git a/src/logging_testing_6/auth/AuthService.java b/src/logging_testing_6/auth/AuthService.java new file mode 100644 index 0000000..8a47ee4 --- /dev/null +++ b/src/logging_testing_6/auth/AuthService.java @@ -0,0 +1,6 @@ +package logging_testing_6.auth; + +public interface AuthService { + + boolean authUser(String username, String password); +} diff --git a/src/logging_testing_6/auth/AuthServiceImpl.java b/src/logging_testing_6/auth/AuthServiceImpl.java new file mode 100644 index 0000000..03794ba --- /dev/null +++ b/src/logging_testing_6/auth/AuthServiceImpl.java @@ -0,0 +1,13 @@ +package logging_testing_6.auth; + +import logging_testing_6.dbconn.JdbcExample; + +public class AuthServiceImpl implements AuthService { + + @Override + public boolean authUser(String username, String password) { + String pwd = JdbcExample.readFromDB(username); + + return pwd != null && pwd.equals(password); + } +} diff --git a/src/logging_testing_6/dbconn/JdbcExample.java b/src/logging_testing_6/dbconn/JdbcExample.java new file mode 100644 index 0000000..05268f9 --- /dev/null +++ b/src/logging_testing_6/dbconn/JdbcExample.java @@ -0,0 +1,44 @@ +package logging_testing_6.dbconn; + +import java.sql.*; + +public class JdbcExample { + private static final String URL = "jdbc:sqlite:database.db"; + + static { + try { + Class.forName("org.sqlite.JDBC"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } + + public static String readFromDB(String clientName) { + String password = null; + try (Connection connection = DriverManager.getConnection(URL)) { + PreparedStatement preparedStatement = connection.prepareStatement("SELECT u.name, p.password FROM users u INNER JOIN passwords p on u.id = p.id WHERE u.name = ?"); + preparedStatement.setString(1, clientName); + ResultSet resultSet = preparedStatement.executeQuery(); + while (resultSet.next()) { +// String name = resultSet.getString("name"); + password = resultSet.getString("password"); + } + } catch (SQLException e) { + e.printStackTrace(); + } + System.out.println("returned password: " + password); + return password; + } + + public static void changeNick(String oldName, String newName) { + try (Connection connection = DriverManager.getConnection(URL)) { + PreparedStatement preparedStatement = connection.prepareStatement("UPDATE users SET name = ? WHERE name = ?;"); + preparedStatement.setString(1, newName); + preparedStatement.setString(2, oldName); + preparedStatement.executeUpdate(); + System.out.println("Name " + oldName + " changed to: " + newName); + } catch (SQLException e) { + e.printStackTrace(); + } + } +} diff --git a/src/logging_testing_6/gui/App.java b/src/logging_testing_6/gui/App.java new file mode 100644 index 0000000..2027773 --- /dev/null +++ b/src/logging_testing_6/gui/App.java @@ -0,0 +1,17 @@ +package logging_testing_6.gui; + +import javax.swing.*; + +public class App { + + private static MainWindow mainWindow; + + public static void main(String[] args) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + mainWindow = new MainWindow(); + } + }); + } +} diff --git a/src/logging_testing_6/gui/AuthException.java b/src/logging_testing_6/gui/AuthException.java new file mode 100644 index 0000000..974d389 --- /dev/null +++ b/src/logging_testing_6/gui/AuthException.java @@ -0,0 +1,4 @@ +package logging_testing_6.gui; + +public class AuthException extends RuntimeException { +} diff --git a/src/logging_testing_6/gui/LoginDialog.java b/src/logging_testing_6/gui/LoginDialog.java new file mode 100644 index 0000000..1f107ce --- /dev/null +++ b/src/logging_testing_6/gui/LoginDialog.java @@ -0,0 +1,107 @@ +package logging_testing_6.gui; + +import javax.swing.*; +import javax.swing.border.LineBorder; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.IOException; + + +public class LoginDialog extends JDialog { + + private JTextField tfUsername; + private JPasswordField pfPassword; + private JLabel lbUsername; + private JLabel lbPassword; + private JButton btnLogin; + private JButton btnCancel; + + private final Network network; + + private boolean connected; + + public LoginDialog(Frame parent, Network network) { + super(parent, "Login", true); + + this.network = network; + this.connected = false; + + JPanel panel = new JPanel(new GridBagLayout()); + GridBagConstraints cs = new GridBagConstraints(); + + cs.fill = GridBagConstraints.HORIZONTAL; + + lbUsername = new JLabel("Username: "); + cs.gridx = 0; + cs.gridy = 0; + cs.gridwidth = 1; + panel.add(lbUsername, cs); + + tfUsername = new JTextField(20); + cs.gridx = 1; + cs.gridy = 0; + cs.gridwidth = 2; + panel.add(tfUsername, cs); + + lbPassword = new JLabel("Password: "); + cs.gridx = 0; + cs.gridy = 1; + cs.gridwidth = 1; + panel.add(lbPassword, cs); + + pfPassword = new JPasswordField(20); + cs.gridx = 1; + cs.gridy = 1; + cs.gridwidth = 2; + panel.add(pfPassword, cs); + panel.setBorder(new LineBorder(Color.GRAY)); + + btnLogin = new JButton("Login"); + btnCancel = new JButton("Cancel"); + + JPanel bp = new JPanel(); + bp.add(btnLogin); + btnLogin.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + try { + network.authorize(tfUsername.getText(), String.valueOf(pfPassword.getPassword())); + connected = true; + } catch (AuthException ex) { + JOptionPane.showMessageDialog(LoginDialog.this, + "Ошибка авторизации", + "Авторизация", + JOptionPane.ERROR_MESSAGE); + return; + } catch (IOException ex) { + JOptionPane.showMessageDialog(LoginDialog.this, + "Ошибка сети", + "Авторизация", + JOptionPane.ERROR_MESSAGE); + return; + } + dispose(); + } + }); + + bp.add(btnCancel); + btnCancel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + dispose(); + } + }); + + getContentPane().add(panel, BorderLayout.CENTER); + getContentPane().add(bp, BorderLayout.PAGE_END); + + pack(); + setResizable(false); + setLocationRelativeTo(parent); + } + + public boolean isConnected() { + return connected; + } +} diff --git a/src/logging_testing_6/gui/MainWindow.java b/src/logging_testing_6/gui/MainWindow.java new file mode 100644 index 0000000..bd08833 --- /dev/null +++ b/src/logging_testing_6/gui/MainWindow.java @@ -0,0 +1,155 @@ +package logging_testing_6.gui; + +import logging_testing_6.gui.elements_for_changing_name.DialogWindow; +import logging_testing_6.history.HistoryKeeper; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; +import java.util.List; + +public class MainWindow extends JFrame implements MessageSender { + + private JTextField textField; + private JButton button; + private JScrollPane scrollPane; + private JList messageList; + private DefaultListModel messageListModel; + private JList userList; + private JPanel panel; + + private Network network; + + public MainWindow() { + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + setBounds(200, 200, 500, 500); + + // Добавим контекстное меню + JPopupMenu popupMenu = createPopupMenu(); + ((JComponent) getContentPane()).setComponentPopupMenu(popupMenu); + + setLayout(new BorderLayout()); + + messageListModel = new DefaultListModel<>(); + messageList = new JList<>(messageListModel); + messageList.setCellRenderer(new MessageCellRenderer()); + + panel = new JPanel(); + panel.setLayout(new BorderLayout()); + panel.add(messageList, BorderLayout.SOUTH); + panel.setBackground(messageList.getBackground()); + scrollPane = new JScrollPane(panel); + scrollPane.setInheritsPopupMenu(true); // Добавим контекстное меню + add(scrollPane, BorderLayout.CENTER); + + userList = new JList<>(); + // TODO добавить класс Model для userList по аналогии с messageListModel + userList.setListData(new String[]{"ivan", "petr", "julia"}); // Для простоты, пока фиксированный список имен пользователей + userList.setPreferredSize(new Dimension(100, 0)); + add(userList, BorderLayout.WEST); + userList.setInheritsPopupMenu(true); // Добавим контекстное меню + + textField = new JTextField(); + button = new JButton("Отправить"); + button.addActionListener(new AbstractAction() { + @Override + public void actionPerformed(ActionEvent e) { + String userTo = userList.getSelectedValue(); + if (userTo == null) { + JOptionPane.showMessageDialog(MainWindow.this, + "Не указан получатель", + "Отправка сообщения", + JOptionPane.ERROR_MESSAGE); + return; + } + + String text = textField.getText(); + if (text == null || text.trim().isEmpty()) { + JOptionPane.showMessageDialog(MainWindow.this, + "Нельзя отправить пустое сообщение", + "Отправка сообщения", + JOptionPane.ERROR_MESSAGE); + return; + } + + Message msg = new Message(network.getUsername(), userTo, text.trim()); + submitMessage(msg); + textField.setText(null); + textField.requestFocus(); + + network.sendMessageToUser(msg); + } + }); + + this.addComponentListener(new ComponentAdapter() { + @Override + public void componentResized(ComponentEvent evt) { + messageList.ensureIndexIsVisible(messageListModel.size() - 1); + } + }); + + panel = new JPanel(); + panel.setLayout(new BorderLayout()); + panel.add(button, BorderLayout.EAST); + panel.add(textField, BorderLayout.CENTER); + + add(panel, BorderLayout.SOUTH); + + addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + if (network != null) { + network.close(); + } + super.windowClosing(e); + } + }); + + + setVisible(true); + + network = new Network("localhost", 7777, this); + + LoginDialog loginDialog = new LoginDialog(this, network); + loginDialog.setVisible(true); + + if (!loginDialog.isConnected()) { + System.exit(0); + } + + setTitle("Сетевой чат. Пользователь " + network.getUsername()); + + List listMessages = HistoryKeeper.getHistory(network.getUsername() + ".txt", 10); + for (Message message : listMessages) { + messageListModel.addElement(message); + } + } + + @Override + public void submitMessage(Message msg) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + messageListModel.add(messageListModel.size(), msg); + messageList.ensureIndexIsVisible(messageListModel.size() - 1); + } + }); + } + + private JPopupMenu createPopupMenu() { + JPopupMenu popupMenu = new JPopupMenu(); + JMenuItem changeNameItem = new JMenuItem("Сменить ник"); + changeNameItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + DialogWindow dialogWindow = new DialogWindow(getNetwork()); + } + }); + popupMenu.add(changeNameItem); + return popupMenu; + } + + public Network getNetwork() { + return network; + } +} diff --git a/src/logging_testing_6/gui/Message.java b/src/logging_testing_6/gui/Message.java new file mode 100644 index 0000000..aa77eb9 --- /dev/null +++ b/src/logging_testing_6/gui/Message.java @@ -0,0 +1,37 @@ +package logging_testing_6.gui; + +import java.time.LocalDate; + +public class Message { + + private String userFrom; + + private String userTo; + + private String text; + + private LocalDate date; + + public Message(String userFrom, String userTo, String text) { + this.userFrom = userFrom; + this.userTo = userTo; + this.text = text; + this.date = LocalDate.now(); + } + + public String getUserFrom() { + return userFrom; + } + + public String getUserTo() { + return userTo; + } + + public String getText() { + return text; + } + + public LocalDate getDate() { + return date; + } +} diff --git a/src/logging_testing_6/gui/MessageCellRenderer.java b/src/logging_testing_6/gui/MessageCellRenderer.java new file mode 100644 index 0000000..171be89 --- /dev/null +++ b/src/logging_testing_6/gui/MessageCellRenderer.java @@ -0,0 +1,32 @@ +package logging_testing_6.gui; + +import javax.swing.*; +import java.awt.*; + +public class MessageCellRenderer extends JPanel implements ListCellRenderer { + + private JLabel userName; + + private JLabel message; + + public MessageCellRenderer() { + super(); + setLayout(new BorderLayout()); + userName = new JLabel(); + Font f = userName.getFont(); + userName.setFont(f.deriveFont(f.getStyle() | Font.BOLD)); + message = new JLabel(); + add(userName, BorderLayout.NORTH); + add(message, BorderLayout.SOUTH); + } + + @Override + public Component getListCellRendererComponent(JList list, Message value, + int index, boolean isSelected, boolean cellHasFocus) { + setBackground(list.getBackground()); + userName.setOpaque(true); + userName.setText(value.getUserFrom()); + message.setText(value.getText()); + return this; + } +} diff --git a/src/logging_testing_6/gui/MessageSender.java b/src/logging_testing_6/gui/MessageSender.java new file mode 100644 index 0000000..4c85248 --- /dev/null +++ b/src/logging_testing_6/gui/MessageSender.java @@ -0,0 +1,16 @@ +package logging_testing_6.gui; + +/** + * Интерфейс для взаимодействия класса сети {@link Network} + * с пользовательским интерфейсом на + */ +public interface MessageSender { + + /** + * Метод вызывается классом сети при получении нового сообщения + * @param msg новое сообщение + */ + void submitMessage(Message msg); + + // TODO добавить метод для оповещения о новом пользователе +} diff --git a/src/logging_testing_6/gui/Network.java b/src/logging_testing_6/gui/Network.java new file mode 100644 index 0000000..e1f300f --- /dev/null +++ b/src/logging_testing_6/gui/Network.java @@ -0,0 +1,108 @@ +package logging_testing_6.gui; + +import java.io.Closeable; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.net.Socket; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class Network implements Closeable { + + private static final String AUTH_PATTERN = "/auth %s %s"; + private static final String MESSAGE_SEND_PATTERN = "/w %s %s"; + private static final String USER_LIST_PATTERN = "/userlist"; + private static final Pattern MESSAGE_PATTERN = Pattern.compile("^/w (\\w+) (.+)", Pattern.MULTILINE); + + private Socket socket; + private DataOutputStream out; + private DataInputStream in; + private final MessageSender messageSender; + private final Thread receiver; + + private String username; + private final String hostName; + private final int port; + + public Network(String hostName, int port, MessageSender messageSender) { + this.hostName = hostName; + this.port = port; + this.messageSender = messageSender; + + this.receiver = createReceiverThread(); + } + + private Thread createReceiverThread() { + return new Thread(new Runnable() { + @Override + public void run() { + while (!Thread.currentThread().isInterrupted()) { + try { + String text = in.readUTF(); + + System.out.println("New message " + text); + Matcher matcher = MESSAGE_PATTERN.matcher(text); + if (matcher.matches()) { + Message msg = new Message(matcher.group(1), username, + matcher.group(2)); + messageSender.submitMessage(msg); + } else if (text.startsWith(USER_LIST_PATTERN)) { + // TODO обновить список подключенных пользователей + } + } catch (IOException e) { + e.printStackTrace(); + } + } + System.out.printf("Network connection is closed for user %s%n", username); + } + }); + } + + public void sendMessageToUser(Message message) { + sendMessage(String.format(MESSAGE_SEND_PATTERN, message.getUserTo(), message.getText())); + } + + private void sendMessage(String msg) { + try { + out.writeUTF(msg); + out.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void authorize(String username, String password) throws IOException { + socket = new Socket(hostName, port); + out = new DataOutputStream(socket.getOutputStream()); + in = new DataInputStream(socket.getInputStream()); + + out.writeUTF(String.format(AUTH_PATTERN, username, password)); + String response = in.readUTF(); + if (response.equals("/auth successful")) { + this.username = username; + receiver.start(); + } else { + throw new AuthException(); + } + } + + public String getUsername() { + return username; + } + + @Override + public void close() { + try { + socket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + receiver.interrupt(); + try { + receiver.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/src/logging_testing_6/gui/elements_for_changing_name/DialogWindow.java b/src/logging_testing_6/gui/elements_for_changing_name/DialogWindow.java new file mode 100644 index 0000000..ec076b6 --- /dev/null +++ b/src/logging_testing_6/gui/elements_for_changing_name/DialogWindow.java @@ -0,0 +1,55 @@ +package logging_testing_6.gui.elements_for_changing_name; + +import logging_testing_6.dbconn.JdbcExample; +import logging_testing_6.gui.Network; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class DialogWindow extends JFrame { + private final Network network; + + public DialogWindow(Network network) { + super("Введите новое имени пользователя"); + this.network = network; + setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); + + JTextField tf = new JTextField("Введите новое имя пользователя"); + + JButton bOK = new JButton("OK"); + bOK.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + String currentUserName = network.getUsername(); + String newUserName = tf.getText(); + JdbcExample.changeNick(currentUserName, newUserName); + dispose(); + } + }); + + JButton bCancel = new JButton("Отмена"); + bCancel.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + dispose(); + } + }); + + JPanel textPanel = new JPanel(); + JPanel buttonPanel = new JPanel(); + buttonPanel.add(bCancel); + buttonPanel.add(bOK); + + textPanel.add(tf); + + JPanel mainPanel = new JPanel(); + mainPanel.setLayout(new GridLayout(2, 1, 5, 10)); + mainPanel.add(textPanel); + mainPanel.add(buttonPanel); + getContentPane().add(mainPanel); + pack(); + setVisible(true); + } +} diff --git a/src/logging_testing_6/history/HistoryKeeper.java b/src/logging_testing_6/history/HistoryKeeper.java new file mode 100644 index 0000000..6132712 --- /dev/null +++ b/src/logging_testing_6/history/HistoryKeeper.java @@ -0,0 +1,84 @@ +package logging_testing_6.history; + +import logging_testing_6.gui.Message; + +import java.io.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class HistoryKeeper { + private static final String HISTORY_DIR = "history"; + + public static void saveMassageToHistoryFrom(Message message) { + String userName = message.getUserFrom(); + String messageText = message.getText(); + String fileName = userName + ".txt"; + File file = new File(HISTORY_DIR, fileName); + try (BufferedWriter out = new BufferedWriter(new FileWriter(file, true)) +// BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8)) + ) { + String str = userName + ": " + messageText + "\r\n"; + out.write(str); + out.flush(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void saveMassageToHistoryTo(Message message) { + String userName = message.getUserTo(); + String messageText = message.getText(); + String fileName = userName + ".txt"; + File file = new File(HISTORY_DIR, fileName); + try (BufferedWriter out = new BufferedWriter(new FileWriter(file, true))) { +// String str = userName + ": " + messageText + "\r\n"; + String str = message.getUserFrom() + ": " + messageText + "\r\n"; + out.write(str); + out.flush(); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static List getHistory(String fileName, int numberOfRows) { + List textMessageList = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + File file = new File(HISTORY_DIR, fileName); + try { + RandomAccessFile raf = new RandomAccessFile(file, "r"); + for (long pointer = file.length() - 1; pointer >= 0 && textMessageList.size() < numberOfRows; pointer--) { + raf.seek(pointer); + char aChar = (char) raf.read(); + if (aChar != '\n') { + sb.append(aChar); + } else if (sb.length() > 1) { + textMessageList.add(sb.reverse().toString()); + sb.delete(0, sb.length()); + } + } + if (sb.length() > 1) { + textMessageList.add(sb.reverse().toString()); + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + List messageList = textMessageList.stream() + .map(record -> { + String userFrom = record.substring(0, record.indexOf(" ") - 1); + String textMessage = record.substring(record.indexOf(" ") - 1); + return new Message(userFrom, null, textMessage); + }).collect(Collectors.toList()); + + Collections.reverse(messageList); + return messageList; + } +} diff --git a/src/test_ex/TestEx.java b/src/test_ex/TestEx.java new file mode 100644 index 0000000..0268ca5 --- /dev/null +++ b/src/test_ex/TestEx.java @@ -0,0 +1,34 @@ +package test_ex; + +import java.util.Arrays; + +public class TestEx { + + public int[] task1(int[] data) { + for (int i = data.length - 1; i >= 0; i--) { + if (data[i] == 4) { + return Arrays.copyOfRange(data, i + 1, data.length); + } + } + throw new RuntimeException("Invalid array"); + } + + public boolean task2(int[] data) { + boolean contains1 = false; + boolean contains2 = false; + + for (int i = 0; i < data.length; i++) { + switch (data[i]) { + case 1: + contains1 = true; + break; + case 4: + contains2 = true; + break; + default: + return false; + } + } + return contains1 && contains2; + } +} \ No newline at end of file diff --git a/src/test_ex/calculator/Calculator.java b/src/test_ex/calculator/Calculator.java new file mode 100644 index 0000000..a5e344e --- /dev/null +++ b/src/test_ex/calculator/Calculator.java @@ -0,0 +1,36 @@ +package test_ex.calculator; + +public class Calculator implements ICalc { + NumberProvader numberProvader; + + public Calculator(NumberProvader numberProvader) { + this.numberProvader = numberProvader; + } + + @Override + public int add(int a, int b) { + return a + b; + } + + @Override + public int sub(int a, int b) { + return a - b; + } + + @Override + public int mul(int a, int b) { + return a * b; + } + + @Override + public int div(int a, int b) { + return a / b; + } + + @Override + public int sumFromProvider() { + int a = numberProvader.getNumber(); + int b = numberProvader.getNumber(); + return add(a, b); + } +} diff --git a/src/test_ex/calculator/ICalc.java b/src/test_ex/calculator/ICalc.java new file mode 100644 index 0000000..f678a1f --- /dev/null +++ b/src/test_ex/calculator/ICalc.java @@ -0,0 +1,9 @@ +package test_ex.calculator; + +public interface ICalc { + int add(int a, int b); + int sub(int a, int b); + int mul(int a, int b); + int div(int a, int b); + int sumFromProvider(); +} diff --git a/src/test_ex/calculator/NumberProvader.java b/src/test_ex/calculator/NumberProvader.java new file mode 100644 index 0000000..b6c4103 --- /dev/null +++ b/src/test_ex/calculator/NumberProvader.java @@ -0,0 +1,5 @@ +package test_ex.calculator; + +public interface NumberProvader { + int getNumber(); +} diff --git a/src/test_ex/calculator/NumberProviderImpl.java b/src/test_ex/calculator/NumberProviderImpl.java new file mode 100644 index 0000000..9be7720 --- /dev/null +++ b/src/test_ex/calculator/NumberProviderImpl.java @@ -0,0 +1,13 @@ +package test_ex.calculator; + +import java.util.Scanner; + +public class NumberProviderImpl implements NumberProvader{ + + @Override + public int getNumber() { + try (Scanner scanner = new Scanner(System.in)){ + return scanner.nextInt(); + } + } +} diff --git a/tests/test_ex/calculator/CalculatorTest4.java b/tests/test_ex/calculator/CalculatorTest4.java new file mode 100644 index 0000000..c1f92b9 --- /dev/null +++ b/tests/test_ex/calculator/CalculatorTest4.java @@ -0,0 +1,69 @@ +package test_ex.calculator; + +import org.hamcrest.core.Is; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +public class CalculatorTest4 { + private ICalc calc; + private NumberProvader numberProvader; + + @Before + public void setUp() { + numberProvader = Mockito.mock(NumberProvader.class); + calc = new Calculator(numberProvader); + } + + @After + public void tearDown() { + calc = null; + } + + @Test + public void test_sum() { + int result = calc.add(2, 2); + Assert.assertEquals(4, result); + } + + @Test + public void test_sum1() { + int result = calc.add(5, 10); + Assert.assertEquals(15, result); + } + + @Test + public void test_sub() { + Assert.assertThat(calc.sub(5, 12), Is.is(-7)); + } + + @Test + public void test_mul() { + Assert.assertThat(calc.mul(2, 3), Is.is(6)); + } + + @Test(timeout = 100L) + public void test_div() { + Assert.assertThat(calc.div(6, 3), Is.is(2)); + } + + @Test + public void test_div1() { + Assert.assertThat(calc.div(0, 3), Is.is(0)); + } + + @Test(expected = ArithmeticException.class) + public void test_div2() { + Assert.assertThat(calc.div(3, 0), Is.is(0)); + } + + @Test + public void testMock() { + Mockito.when(numberProvader.getNumber()) + .thenReturn(2, 6); + int result = calc.sumFromProvider(); + Assert.assertEquals(8, result); + } +} \ No newline at end of file diff --git a/tests/tests/Task2TestJU4.java b/tests/tests/Task2TestJU4.java new file mode 100644 index 0000000..a93b1a7 --- /dev/null +++ b/tests/tests/Task2TestJU4.java @@ -0,0 +1,47 @@ +package tests; + +import org.hamcrest.core.Is; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import test_ex.TestEx; + +public class Task2TestJU4 { + private TestEx dz; + + @Before + public void prepare() { + dz = new TestEx(); + } + + @Test + public void test_task2_empty_array() { + Assert.assertThat(dz.task2(new int[]{}), Is.is(false)); + } + + @Test + public void test_task2_only_1_and_4() { + Assert.assertThat(dz.task2(new int[]{1, 4, 4, 1}), Is.is(true)); + } + + @Test + public void test_task2_1_and_4_and_others() { + Assert.assertThat(dz.task2(new int[]{1, 4, 3, 1}), Is.is(false)); + } + + @Test + public void test_task2_without_1_and_4() { + Assert.assertThat(dz.task2(new int[]{2, 3}), Is.is(false)); + } + + @Test + public void test_task2_only_1() { + Assert.assertThat(dz.task2(new int[]{1, 1, 1}), Is.is(false)); + } + + @Test + public void test_task2_only_4() { +// Assert.assertThat(dz.task2(new int[]{4, 4, 4}), Is.is(false)); + Assert.assertFalse(String.valueOf(dz.task2(new int[]{4, 4, 4})), false); + } +} diff --git a/tests/tests/TestExJU4TestCase.java b/tests/tests/TestExJU4TestCase.java new file mode 100644 index 0000000..f3ecd0b --- /dev/null +++ b/tests/tests/TestExJU4TestCase.java @@ -0,0 +1,37 @@ +package tests; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import test_ex.TestEx; + +public class TestExJU4TestCase { + private TestEx dz; + + @Before + public void prepare() { + dz = new TestEx(); + } + + @Test(expected = RuntimeException.class) + public void test_task1_empty_array() { + dz.task1(new int[0]); + } + + @Test(expected = RuntimeException.class) + public void test_task1_without_4() { + dz.task1(new int[]{1, 2, 3}); + } + + @Test + public void test_task1_where_4_is_not_last() { + int[] data = {1, 2, 3, 4, 5, 6, 7}; + Assert.assertArrayEquals(new int[]{5, 6, 7,}, dz.task1(data)); + } + + @Test + public void test_task1_with_some_4() { + int[] data = {1, 4, 3, 4, 5, 6, 4}; + Assert.assertArrayEquals(new int[]{}, dz.task1(data)); + } +} \ No newline at end of file diff --git a/tests/tests/TestExJU5TestCase.java b/tests/tests/TestExJU5TestCase.java new file mode 100644 index 0000000..4735507 --- /dev/null +++ b/tests/tests/TestExJU5TestCase.java @@ -0,0 +1,66 @@ +package tests; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import test_ex.TestEx; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +class TestExJU5TestCase { + private TestEx dz; + + @BeforeEach + public void prepare() { + dz = new TestEx(); + } + + @Test + void test_task1_empty_array() { + Assertions.assertThrows(RuntimeException.class, () -> { + dz.task1(new int[0]); + }); + } + + @Test + public void test_tsak1_without_4() { + Assertions.assertThrows(RuntimeException.class, () -> dz.task1(new int[]{1, 2, 3})); + } + + @Test + public void test_task1_where_4_is_not_last() { + int[] data = {1, 2, 3, 4, 5, 6, 7}; + Assertions.assertArrayEquals(new int[]{5, 6, 7}, dz.task1(data)); + } + + @Test + public void test_task1_whith_some_4() { + int[] data = {1, 4, 3, 4, 5, 6, 4}; + Assertions.assertArrayEquals(new int[]{}, dz.task1(data)); + } + + @ParameterizedTest + @MethodSource("params") + public void test_task1_whith_params(int[] data, int[] result) { + Assertions.assertArrayEquals(result, dz.task1(data)); + } + + private static Stream params() { + return Stream.of( + Arguments.of( + new int[]{1, 3, 4}, + new int[]{}), + Arguments.of( + new int[]{1, 4, 3, 4}, + new int[]{}), + Arguments.of( + new int[]{1, 4, 3, 5}, + new int[]{3, 5}) + ); + } +} \ No newline at end of file