From ff8c5788e617d897b0cc2fdf67d38e930f1a58a8 Mon Sep 17 00:00:00 2001 From: Robots Date: Wed, 21 Feb 2024 00:55:45 +0500 Subject: [PATCH 1/5] 1, 2 --- .gitignore | 42 ++++- .idea/.gitignore | 3 + .idea/checkstyle-idea.xml | 16 ++ .idea/encodings.xml | 7 + .idea/misc.xml | 14 ++ .idea/vcs.xml | 6 + README.md | 2 - pom.xml | 17 ++ robots/.classpath | 6 - robots/.gitignore | 2 - robots/.project | 17 -- robots/src/gui/MainApplicationFrame.java | 156 ------------------ .../java/org/example}/gui/GameVisualizer.java | 0 .../java/org/example}/gui/GameWindow.java | 0 .../main/java/org/example}/gui/LogWindow.java | 0 .../org/example/gui/MainApplicationFrame.java | 96 +++++++++++ src/main/java/org/example/gui/MenuBar.java | 75 +++++++++ .../java/org/example}/gui/RobotsProgram.java | 0 .../org/example}/log/LogChangeListener.java | 0 .../main/java/org/example}/log/LogEntry.java | 0 .../main/java/org/example}/log/LogLevel.java | 0 .../org/example}/log/LogWindowSource.java | 39 +++-- .../main/java/org/example}/log/Logger.java | 0 23 files changed, 296 insertions(+), 202 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/checkstyle-idea.xml create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml delete mode 100644 README.md create mode 100644 pom.xml delete mode 100644 robots/.classpath delete mode 100644 robots/.gitignore delete mode 100644 robots/.project delete mode 100644 robots/src/gui/MainApplicationFrame.java rename {robots/src => src/main/java/org/example}/gui/GameVisualizer.java (100%) rename {robots/src => src/main/java/org/example}/gui/GameWindow.java (100%) rename {robots/src => src/main/java/org/example}/gui/LogWindow.java (100%) create mode 100644 src/main/java/org/example/gui/MainApplicationFrame.java create mode 100644 src/main/java/org/example/gui/MenuBar.java rename {robots/src => src/main/java/org/example}/gui/RobotsProgram.java (100%) rename {robots/src => src/main/java/org/example}/log/LogChangeListener.java (100%) rename {robots/src => src/main/java/org/example}/log/LogEntry.java (100%) rename {robots/src => src/main/java/org/example}/log/LogLevel.java (100%) rename {robots/src => src/main/java/org/example}/log/LogWindowSource.java (74%) rename {robots/src => src/main/java/org/example}/log/Logger.java (100%) diff --git a/.gitignore b/.gitignore index 2c6eb3893..5ff6309b7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,38 @@ -/.metadata -/robots/.settings -/robots/bin -eclipse.bat \ No newline at end of file +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 000000000..26d33521a --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/checkstyle-idea.xml b/.idea/checkstyle-idea.xml new file mode 100644 index 000000000..c5dcbb130 --- /dev/null +++ b/.idea/checkstyle-idea.xml @@ -0,0 +1,16 @@ + + + + 10.13.0 + JavaOnly + true + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 000000000..aa00ffab7 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 000000000..efc412a34 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 000000000..35eb1ddfb --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md deleted file mode 100644 index d79735aee..000000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# Robots -The project to learn OO design concepts and MDI application development in Java diff --git a/pom.xml b/pom.xml new file mode 100644 index 000000000..b5b3c37ee --- /dev/null +++ b/pom.xml @@ -0,0 +1,17 @@ + + + 4.0.0 + + org.example + Robots + 1.0-SNAPSHOT + + + 20 + 20 + UTF-8 + + + \ No newline at end of file diff --git a/robots/.classpath b/robots/.classpath deleted file mode 100644 index fceb4801b..000000000 --- a/robots/.classpath +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/robots/.gitignore b/robots/.gitignore deleted file mode 100644 index 2757ffa76..000000000 --- a/robots/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/bin/ -/.settings/ diff --git a/robots/.project b/robots/.project deleted file mode 100644 index 78e165663..000000000 --- a/robots/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - Robots - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/robots/src/gui/MainApplicationFrame.java b/robots/src/gui/MainApplicationFrame.java deleted file mode 100644 index 62e943ee1..000000000 --- a/robots/src/gui/MainApplicationFrame.java +++ /dev/null @@ -1,156 +0,0 @@ -package gui; - -import java.awt.Dimension; -import java.awt.Toolkit; -import java.awt.event.KeyEvent; - -import javax.swing.JDesktopPane; -import javax.swing.JFrame; -import javax.swing.JInternalFrame; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.SwingUtilities; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; - -import log.Logger; - -/** - * Что требуется сделать: - * 1. Метод создания меню перегружен функционалом и трудно читается. - * Следует разделить его на серию более простых методов (или вообще выделить отдельный класс). - * - */ -public class MainApplicationFrame extends JFrame -{ - private final JDesktopPane desktopPane = new JDesktopPane(); - - public MainApplicationFrame() { - //Make the big window be indented 50 pixels from each edge - //of the screen. - int inset = 50; - Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); - setBounds(inset, inset, - screenSize.width - inset*2, - screenSize.height - inset*2); - - setContentPane(desktopPane); - - - LogWindow logWindow = createLogWindow(); - addWindow(logWindow); - - GameWindow gameWindow = new GameWindow(); - gameWindow.setSize(400, 400); - addWindow(gameWindow); - - setJMenuBar(generateMenuBar()); - setDefaultCloseOperation(EXIT_ON_CLOSE); - } - - protected LogWindow createLogWindow() - { - LogWindow logWindow = new LogWindow(Logger.getDefaultLogSource()); - logWindow.setLocation(10,10); - logWindow.setSize(300, 800); - setMinimumSize(logWindow.getSize()); - logWindow.pack(); - Logger.debug("Протокол работает"); - return logWindow; - } - - protected void addWindow(JInternalFrame frame) - { - desktopPane.add(frame); - frame.setVisible(true); - } - -// protected JMenuBar createMenuBar() { -// JMenuBar menuBar = new JMenuBar(); -// -// //Set up the lone menu. -// JMenu menu = new JMenu("Document"); -// menu.setMnemonic(KeyEvent.VK_D); -// menuBar.add(menu); -// -// //Set up the first menu item. -// JMenuItem menuItem = new JMenuItem("New"); -// menuItem.setMnemonic(KeyEvent.VK_N); -// menuItem.setAccelerator(KeyStroke.getKeyStroke( -// KeyEvent.VK_N, ActionEvent.ALT_MASK)); -// menuItem.setActionCommand("new"); -//// menuItem.addActionListener(this); -// menu.add(menuItem); -// -// //Set up the second menu item. -// menuItem = new JMenuItem("Quit"); -// menuItem.setMnemonic(KeyEvent.VK_Q); -// menuItem.setAccelerator(KeyStroke.getKeyStroke( -// KeyEvent.VK_Q, ActionEvent.ALT_MASK)); -// menuItem.setActionCommand("quit"); -//// menuItem.addActionListener(this); -// menu.add(menuItem); -// -// return menuBar; -// } - - private JMenuBar generateMenuBar() - { - JMenuBar menuBar = new JMenuBar(); - - JMenu lookAndFeelMenu = new JMenu("Режим отображения"); - lookAndFeelMenu.setMnemonic(KeyEvent.VK_V); - lookAndFeelMenu.getAccessibleContext().setAccessibleDescription( - "Управление режимом отображения приложения"); - - { - JMenuItem systemLookAndFeel = new JMenuItem("Системная схема", KeyEvent.VK_S); - systemLookAndFeel.addActionListener((event) -> { - setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - this.invalidate(); - }); - lookAndFeelMenu.add(systemLookAndFeel); - } - - { - JMenuItem crossplatformLookAndFeel = new JMenuItem("Универсальная схема", KeyEvent.VK_S); - crossplatformLookAndFeel.addActionListener((event) -> { - setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); - this.invalidate(); - }); - lookAndFeelMenu.add(crossplatformLookAndFeel); - } - - JMenu testMenu = new JMenu("Тесты"); - testMenu.setMnemonic(KeyEvent.VK_T); - testMenu.getAccessibleContext().setAccessibleDescription( - "Тестовые команды"); - - { - JMenuItem addLogMessageItem = new JMenuItem("Сообщение в лог", KeyEvent.VK_S); - addLogMessageItem.addActionListener((event) -> { - Logger.debug("Новая строка"); - }); - testMenu.add(addLogMessageItem); - } - - menuBar.add(lookAndFeelMenu); - menuBar.add(testMenu); - return menuBar; - } - - private void setLookAndFeel(String className) - { - try - { - UIManager.setLookAndFeel(className); - SwingUtilities.updateComponentTreeUI(this); - } - catch (ClassNotFoundException | InstantiationException - | IllegalAccessException | UnsupportedLookAndFeelException e) - { - // just ignore - } - } -} diff --git a/robots/src/gui/GameVisualizer.java b/src/main/java/org/example/gui/GameVisualizer.java similarity index 100% rename from robots/src/gui/GameVisualizer.java rename to src/main/java/org/example/gui/GameVisualizer.java diff --git a/robots/src/gui/GameWindow.java b/src/main/java/org/example/gui/GameWindow.java similarity index 100% rename from robots/src/gui/GameWindow.java rename to src/main/java/org/example/gui/GameWindow.java diff --git a/robots/src/gui/LogWindow.java b/src/main/java/org/example/gui/LogWindow.java similarity index 100% rename from robots/src/gui/LogWindow.java rename to src/main/java/org/example/gui/LogWindow.java diff --git a/src/main/java/org/example/gui/MainApplicationFrame.java b/src/main/java/org/example/gui/MainApplicationFrame.java new file mode 100644 index 000000000..6918f4ae3 --- /dev/null +++ b/src/main/java/org/example/gui/MainApplicationFrame.java @@ -0,0 +1,96 @@ +package gui; + +import java.awt.Dimension; +import java.awt.Toolkit; + +import javax.swing.JDesktopPane; +import javax.swing.JFrame; +import javax.swing.JInternalFrame; + + +import log.Logger; + + + +/** + * Что требуется сделать: + * 1. Метод создания меню перегружен функционалом и трудно читается. + * Следует разделить его на серию более простых методов (или вообще выделить отдельный класс). + * + */ + +public class MainApplicationFrame extends JFrame // +{ + private final JDesktopPane desktopPane = new JDesktopPane(); + + public MainApplicationFrame() { + //Make the big window be indented 50 pixels from each edge + //of the screen. + int inset = 50; + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + setBounds(inset, inset, + screenSize.width - inset*2, + screenSize.height - inset*2); + + setContentPane(desktopPane); + + + LogWindow logWindow = createLogWindow(); + addWindow(logWindow); + + GameWindow gameWindow = new GameWindow(); + gameWindow.setSize(400, 400); + addWindow(gameWindow); + MenuBar menuBar = new MenuBar(this); + setJMenuBar(menuBar.generateMenuBar()); + setDefaultCloseOperation(EXIT_ON_CLOSE); + } + + protected LogWindow createLogWindow() + { + LogWindow logWindow = new LogWindow(Logger.getDefaultLogSource()); + logWindow.setLocation(10,10); + logWindow.setSize(300, 800); + setMinimumSize(logWindow.getSize()); + logWindow.pack(); + Logger.debug("Протокол работает"); + return logWindow; + } + + protected void addWindow(JInternalFrame frame) + { + desktopPane.add(frame); + frame.setVisible(true); + } + +// protected JMenuBar createMenuBar() { +// JMenuBar menuBar = new JMenuBar(); +// +// //Set up the lone menu.(Настройте одиночное меню.) +// JMenu menu = new JMenu("Document"); +// menu.setMnemonic(KeyEvent.VK_D); +// menuBar.add(menu); +// +// //Set up the first menu item.(Настройте первый пункт меню.) +// JMenuItem menuItem = new JMenuItem("New"); +// menuItem.setMnemonic(KeyEvent.VK_N); +// menuItem.setAccelerator(KeyStroke.getKeyStroke( +// KeyEvent.VK_N, ActionEvent.ALT_MASK)); +// menuItem.setActionCommand("new"); +//// menuItem.addActionListener(this); +// menu.add(menuItem); +// +// //Set up the second menu item.(Настройте второй пункт меню.) +// menuItem = new JMenuItem("Quit"); +// menuItem.setMnemonic(KeyEvent.VK_Q); +// menuItem.setAccelerator(KeyStroke.getKeyStroke( +// KeyEvent.VK_Q, ActionEvent.ALT_MASK)); +// menuItem.setActionCommand("quit"); +//// menuItem.addActionListener(this); +// menu.add(menuItem); +// +// return menuBar; +// } + + +} diff --git a/src/main/java/org/example/gui/MenuBar.java b/src/main/java/org/example/gui/MenuBar.java new file mode 100644 index 000000000..a59a24b78 --- /dev/null +++ b/src/main/java/org/example/gui/MenuBar.java @@ -0,0 +1,75 @@ +package gui; + +import log.Logger; + +import javax.swing.*; +import java.awt.event.KeyEvent; + +public class MenuBar { + static MainApplicationFrame mainFrame; // Используем вместо this, т.к. this имеет ввиду MainApplicationFrame объект + // определяем MenuBar передавая ему аргумент MainApplicationFrame + public MenuBar(MainApplicationFrame mainFrame){ + this.mainFrame = mainFrame; + } + public static JMenuBar generateMenuBar() { + JMenuBar menuBar = new JMenuBar(); + + menuBar.add(createLookAndFeelMenu()); + menuBar.add(createTestMenu()); + return menuBar; + } + + private static JMenu createLookAndFeelMenu() { + + JMenu lookAndFeelMenu = new JMenu("Режим отображения"); + lookAndFeelMenu.setMnemonic(KeyEvent.VK_V); + lookAndFeelMenu.getAccessibleContext().setAccessibleDescription( + "Управление режимом отображения приложения"); + + { + JMenuItem systemLookAndFeel = new JMenuItem("Системная схема", KeyEvent.VK_S); + systemLookAndFeel.addActionListener((event) -> { + setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + mainFrame.invalidate(); + }); + lookAndFeelMenu.add(systemLookAndFeel); + } + + { + JMenuItem crossplatformLookAndFeel = new JMenuItem("Универсальная схема", KeyEvent.VK_S); + crossplatformLookAndFeel.addActionListener((event) -> { + setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName()); + mainFrame.invalidate(); + }); + lookAndFeelMenu.add(crossplatformLookAndFeel); + } + + return lookAndFeelMenu; + } + + + private static JMenu createTestMenu() { + JMenu testMenu = new JMenu("Тесты"); + testMenu.setMnemonic(KeyEvent.VK_T); + testMenu.getAccessibleContext().setAccessibleDescription( + "Тестовые команды"); + + { + JMenuItem addLogMessageItem = new JMenuItem("Сообщение в лог", KeyEvent.VK_S); + addLogMessageItem.addActionListener((event) -> { + Logger.debug("Новая строка"); + }); + testMenu.add(addLogMessageItem); + } + return testMenu; + } + + private static void setLookAndFeel(String className) { + try { + UIManager.setLookAndFeel(className); + SwingUtilities.updateComponentTreeUI(mainFrame); + } catch (ClassNotFoundException | InstantiationException + | IllegalAccessException | UnsupportedLookAndFeelException e) { + } + } +} diff --git a/robots/src/gui/RobotsProgram.java b/src/main/java/org/example/gui/RobotsProgram.java similarity index 100% rename from robots/src/gui/RobotsProgram.java rename to src/main/java/org/example/gui/RobotsProgram.java diff --git a/robots/src/log/LogChangeListener.java b/src/main/java/org/example/log/LogChangeListener.java similarity index 100% rename from robots/src/log/LogChangeListener.java rename to src/main/java/org/example/log/LogChangeListener.java diff --git a/robots/src/log/LogEntry.java b/src/main/java/org/example/log/LogEntry.java similarity index 100% rename from robots/src/log/LogEntry.java rename to src/main/java/org/example/log/LogEntry.java diff --git a/robots/src/log/LogLevel.java b/src/main/java/org/example/log/LogLevel.java similarity index 100% rename from robots/src/log/LogLevel.java rename to src/main/java/org/example/log/LogLevel.java diff --git a/robots/src/log/LogWindowSource.java b/src/main/java/org/example/log/LogWindowSource.java similarity index 74% rename from robots/src/log/LogWindowSource.java rename to src/main/java/org/example/log/LogWindowSource.java index ca0ce4426..e24f5b5ed 100644 --- a/robots/src/log/LogWindowSource.java +++ b/src/main/java/org/example/log/LogWindowSource.java @@ -1,32 +1,35 @@ package log; +import java.util.AbstractQueue; import java.util.ArrayList; import java.util.Collections; +import java.util.Queue; +import java.util.concurrent.ArrayBlockingQueue; /** * Что починить: * 1. Этот класс порождает утечку ресурсов (связанные слушатели оказываются * удерживаемыми в памяти) - * 2. Этот класс хранит активные сообщения лога, но в такой реализации он - * их лишь накапливает. Надо же, чтобы количество сообщений в логе было ограничено - * величиной m_iQueueLength (т.е. реально нужна очередь сообщений - * ограниченного размера) + * 2. Этот класс хранит активные сообщения лога, но в такой реализации он + * их лишь накапливает. Надо же, чтобы количество сообщений в логе было ограничено + * величиной m_iQueueLength (т.е. реально нужна очередь сообщений + * ограниченного размера) */ public class LogWindowSource { private int m_iQueueLength; - - private ArrayList m_messages; + + private Queue m_messages; private final ArrayList m_listeners; private volatile LogChangeListener[] m_activeListeners; - - public LogWindowSource(int iQueueLength) + + public LogWindowSource(int iQueueLength) { m_iQueueLength = iQueueLength; - m_messages = new ArrayList(iQueueLength); + m_messages = new ArrayBlockingQueue<>(iQueueLength); // блокирующая очередь m_listeners = new ArrayList(); } - + public void registerListener(LogChangeListener listener) { synchronized(m_listeners) @@ -35,7 +38,7 @@ public void registerListener(LogChangeListener listener) m_activeListeners = null; } } - + public void unregisterListener(LogChangeListener listener) { synchronized(m_listeners) @@ -44,11 +47,16 @@ public void unregisterListener(LogChangeListener listener) m_activeListeners = null; } } - + public void append(LogLevel logLevel, String strMessage) { LogEntry entry = new LogEntry(logLevel, strMessage); - m_messages.add(entry); + synchronized (m_messages){ + if (m_messages.size() > m_iQueueLength){ + m_messages.poll(); // peek() - извлекает, но не удаляет; pool() - извлекает и удаляет заголовок + } + m_messages.add(entry); + } LogChangeListener [] activeListeners = m_activeListeners; if (activeListeners == null) { @@ -61,12 +69,13 @@ public void append(LogLevel logLevel, String strMessage) } } } + assert m_activeListeners != null; for (LogChangeListener listener : activeListeners) { listener.onLogChanged(); } } - + public int size() { return m_messages.size(); @@ -79,7 +88,7 @@ public Iterable range(int startFrom, int count) return Collections.emptyList(); } int indexTo = Math.min(startFrom + count, m_messages.size()); - return m_messages.subList(startFrom, indexTo); + return m_messages.stream().toList().subList(startFrom, indexTo); } public Iterable all() diff --git a/robots/src/log/Logger.java b/src/main/java/org/example/log/Logger.java similarity index 100% rename from robots/src/log/Logger.java rename to src/main/java/org/example/log/Logger.java From 1f5794b2433161408e7670ea58a43dfa44f6993b Mon Sep 17 00:00:00 2001 From: Robots Date: Sun, 25 Feb 2024 17:14:56 +0500 Subject: [PATCH 2/5] 1 --- .idea/misc.xml | 1 - .idea/uiDesigner.xml | 124 ++++++++++++++++++ .idea/vcs.xml | 2 +- src/main/java/org/example/gui/GameWindow.java | 53 +++++++- src/main/java/org/example/gui/LogWindow.java | 59 ++++++++- .../org/example/gui/MainApplicationFrame.java | 9 +- src/main/java/org/example/gui/MenuBar.java | 46 +++++++ .../org/example/resourses/english.properties | 1 + .../org/example/resourses/russian.properties | 1 + 9 files changed, 281 insertions(+), 15 deletions(-) create mode 100644 .idea/uiDesigner.xml create mode 100644 src/main/java/org/example/resourses/english.properties create mode 100644 src/main/java/org/example/resourses/russian.properties diff --git a/.idea/misc.xml b/.idea/misc.xml index efc412a34..2a60c9b9c 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 000000000..2b63946d5 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 35eb1ddfb..94a25f7f4 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/src/main/java/org/example/gui/GameWindow.java b/src/main/java/org/example/gui/GameWindow.java index ecb63c00f..68f6d1181 100644 --- a/src/main/java/org/example/gui/GameWindow.java +++ b/src/main/java/org/example/gui/GameWindow.java @@ -2,8 +2,9 @@ import java.awt.BorderLayout; -import javax.swing.JInternalFrame; -import javax.swing.JPanel; +import javax.swing.*; +import javax.swing.event.InternalFrameEvent; +import javax.swing.event.InternalFrameListener; public class GameWindow extends JInternalFrame { @@ -12,6 +13,54 @@ public GameWindow() { super("Игровое поле", true, true, true, true); m_visualizer = new GameVisualizer(); + setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE); + addInternalFrameListener(new InternalFrameListener() { + + @Override + public void internalFrameOpened(InternalFrameEvent e) { + + } + + @Override + public void internalFrameClosing(InternalFrameEvent event) { + Object[] options = { "Да", "Нет!" }; + int n = JOptionPane + .showOptionDialog(event.getInternalFrame(), "Закрыть окно?", + "Подтверждение", JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE, null, options, + options[0]); + if (n == 0) { + event.getInternalFrame().setVisible(false); + setDefaultCloseOperation(EXIT_ON_CLOSE); + } + } + + @Override + public void internalFrameClosed(InternalFrameEvent e) { + + } + + @Override + public void internalFrameIconified(InternalFrameEvent e) { + + } + + @Override + public void internalFrameDeiconified(InternalFrameEvent e) { + + } + + @Override + public void internalFrameActivated(InternalFrameEvent e) { + + } + + @Override + public void internalFrameDeactivated(InternalFrameEvent e) { + + } + + }); JPanel panel = new JPanel(new BorderLayout()); panel.add(m_visualizer, BorderLayout.CENTER); getContentPane().add(panel); diff --git a/src/main/java/org/example/gui/LogWindow.java b/src/main/java/org/example/gui/LogWindow.java index 723d3e2fc..b1d567c2e 100644 --- a/src/main/java/org/example/gui/LogWindow.java +++ b/src/main/java/org/example/gui/LogWindow.java @@ -1,11 +1,11 @@ package gui; -import java.awt.BorderLayout; -import java.awt.EventQueue; -import java.awt.TextArea; +import java.awt.*; +import java.awt.event.WindowListener; -import javax.swing.JInternalFrame; -import javax.swing.JPanel; +import javax.swing.*; +import javax.swing.event.InternalFrameEvent; +import javax.swing.event.InternalFrameListener; import log.LogChangeListener; import log.LogEntry; @@ -23,7 +23,54 @@ public LogWindow(LogWindowSource logSource) m_logSource.registerListener(this); m_logContent = new TextArea(""); m_logContent.setSize(200, 500); - + setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE); + addInternalFrameListener(new InternalFrameListener() { + + @Override + public void internalFrameOpened(InternalFrameEvent e) { + + } + + @Override + public void internalFrameClosing(InternalFrameEvent event) { + Object[] options = { "Да", "Нет!" }; + int n = JOptionPane + .showOptionDialog(event.getInternalFrame(), "Закрыть окно?", + "Подтверждение", JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE, null, options, + options[0]); + if (n == 0) { + event.getInternalFrame().setVisible(false); + setDefaultCloseOperation(EXIT_ON_CLOSE); + } + } + + @Override + public void internalFrameClosed(InternalFrameEvent e) { + + } + + @Override + public void internalFrameIconified(InternalFrameEvent e) { + + } + + @Override + public void internalFrameDeiconified(InternalFrameEvent e) { + + } + + @Override + public void internalFrameActivated(InternalFrameEvent e) { + + } + + @Override + public void internalFrameDeactivated(InternalFrameEvent e) { + + } + + }); JPanel panel = new JPanel(new BorderLayout()); panel.add(m_logContent, BorderLayout.CENTER); getContentPane().add(panel); diff --git a/src/main/java/org/example/gui/MainApplicationFrame.java b/src/main/java/org/example/gui/MainApplicationFrame.java index 6918f4ae3..112899997 100644 --- a/src/main/java/org/example/gui/MainApplicationFrame.java +++ b/src/main/java/org/example/gui/MainApplicationFrame.java @@ -1,11 +1,9 @@ package gui; -import java.awt.Dimension; -import java.awt.Toolkit; +import java.awt.*; +import java.awt.event.WindowAdapter; -import javax.swing.JDesktopPane; -import javax.swing.JFrame; -import javax.swing.JInternalFrame; +import javax.swing.*; import log.Logger; @@ -41,6 +39,7 @@ public MainApplicationFrame() { GameWindow gameWindow = new GameWindow(); gameWindow.setSize(400, 400); addWindow(gameWindow); + MenuBar menuBar = new MenuBar(this); setJMenuBar(menuBar.generateMenuBar()); setDefaultCloseOperation(EXIT_ON_CLOSE); diff --git a/src/main/java/org/example/gui/MenuBar.java b/src/main/java/org/example/gui/MenuBar.java index a59a24b78..fae7d6134 100644 --- a/src/main/java/org/example/gui/MenuBar.java +++ b/src/main/java/org/example/gui/MenuBar.java @@ -3,7 +3,11 @@ import log.Logger; import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.KeyEvent; +import java.util.Locale; +import java.util.ResourceBundle; public class MenuBar { static MainApplicationFrame mainFrame; // Используем вместо this, т.к. this имеет ввиду MainApplicationFrame объект @@ -16,9 +20,36 @@ public static JMenuBar generateMenuBar() { menuBar.add(createLookAndFeelMenu()); menuBar.add(createTestMenu()); + //menuBar.add(createSettingMenu()); + menuBar.add(createExitButton()); return menuBar; } +// private static JMenu createSettingMenu() { +// ResourceBundle rb = ResourceBundle.getBundle("Language", Locale.getDefault()); +// JMenu settingMenu = new JMenu("Настройки"); +// JMenu settingLanguage = new JMenu(rb.getString("Language")); +// Locale ru = new Locale("ru", "RU"); +// Locale en = new Locale("en", "US"); +// +// { +// JMenuItem english = new JMenuItem("Английский", KeyEvent.VK_S); +// english.addActionListener((event) -> { +// Locale.setDefault(new Locale("en", "US")); +// }); +// settingLanguage.add(english); +// } +// +// { +// JMenuItem russian = new JMenuItem("Русский"); +// +// settingLanguage.add(russian); +// } +// +// settingMenu.add(settingLanguage); +// return settingMenu; +// } + private static JMenu createLookAndFeelMenu() { JMenu lookAndFeelMenu = new JMenu("Режим отображения"); @@ -64,6 +95,21 @@ private static JMenu createTestMenu() { return testMenu; } + private static JMenu createExitButton() { + JMenu menu = new JMenu("Выйти"); + menu.setMnemonic(KeyEvent.VK_T);//KeyEvent.VK_T + + { + JMenuItem exit1 = new JMenuItem("Выйти", KeyEvent.VK_S); + exit1.setFocusable(false); + exit1.addActionListener((event) -> { + System.exit(0); + }); + menu.add(exit1); + } + return menu; + } + private static void setLookAndFeel(String className) { try { UIManager.setLookAndFeel(className); diff --git a/src/main/java/org/example/resourses/english.properties b/src/main/java/org/example/resourses/english.properties new file mode 100644 index 000000000..2dfb3ee48 --- /dev/null +++ b/src/main/java/org/example/resourses/english.properties @@ -0,0 +1 @@ +Language=Language \ No newline at end of file diff --git a/src/main/java/org/example/resourses/russian.properties b/src/main/java/org/example/resourses/russian.properties new file mode 100644 index 000000000..2df80d8cd --- /dev/null +++ b/src/main/java/org/example/resourses/russian.properties @@ -0,0 +1 @@ +Language=\u041F\u0440\u043E\u0442\u043E\u043A\u043E\u043B \u0440\u0430\u0431\u043E\u0442\u044B \ No newline at end of file From cd6dd4a9945fc2caf424e070a120b5a6813a88b7 Mon Sep 17 00:00:00 2001 From: egoribragimov Date: Tue, 12 Mar 2024 08:54:21 +0500 Subject: [PATCH 3/5] =?UTF-8?q?=D0=B1=D0=B0=D0=B3=20=D0=B8=D1=81=D0=BF?= =?UTF-8?q?=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD,=20=D1=81=20=D1=82=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D0=B0=D0=BC=D0=B8=20=D0=BF=D1=80=D0=BE=D0=B1=D0=BB?= =?UTF-8?q?=D0=B5=D0=BC=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/vcs.xml | 2 +- pom.xml | 22 ++++ .../java/org/example/gui/GameVisualizer.java | 3 + .../java/org/example/log/LogWindowSource.java | 110 ++++++++---------- .../testsLogs/LogWindowSourceTest.java | 65 +++++++++++ 5 files changed, 142 insertions(+), 60 deletions(-) create mode 100644 src/main/java/org/example/testsLogs/LogWindowSourceTest.java diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7f4..35eb1ddfb 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/pom.xml b/pom.xml index b5b3c37ee..866f4f846 100644 --- a/pom.xml +++ b/pom.xml @@ -14,4 +14,26 @@ UTF-8 + + + junit + junit + 4.12 + test + + + junit + junit + 4.13.2 + compile + + + org.junit.jupiter + junit-jupiter + RELEASE + compile + + + + \ No newline at end of file diff --git a/src/main/java/org/example/gui/GameVisualizer.java b/src/main/java/org/example/gui/GameVisualizer.java index f82cfd8f8..96952cb29 100644 --- a/src/main/java/org/example/gui/GameVisualizer.java +++ b/src/main/java/org/example/gui/GameVisualizer.java @@ -139,6 +139,9 @@ private void moveRobot(double velocity, double angularVelocity, double duration) { newY = m_robotPositionY + velocity * duration * Math.sin(m_robotDirection); } + // Проверяем, не выходит ли червячок за границы экрана + newX = applyLimits(newX, 0, getWidth()); + newY = applyLimits(newY, 0, getHeight()); m_robotPositionX = newX; m_robotPositionY = newY; double newDirection = asNormalizedRadians(m_robotDirection + angularVelocity * duration); diff --git a/src/main/java/org/example/log/LogWindowSource.java b/src/main/java/org/example/log/LogWindowSource.java index e24f5b5ed..46669444a 100644 --- a/src/main/java/org/example/log/LogWindowSource.java +++ b/src/main/java/org/example/log/LogWindowSource.java @@ -1,10 +1,10 @@ package log; -import java.util.AbstractQueue; + import java.util.ArrayList; -import java.util.Collections; -import java.util.Queue; import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.List; /** * Что починить: @@ -14,85 +14,77 @@ * их лишь накапливает. Надо же, чтобы количество сообщений в логе было ограничено * величиной m_iQueueLength (т.е. реально нужна очередь сообщений * ограниченного размера) + * DONE */ -public class LogWindowSource -{ - private int m_iQueueLength; - - private Queue m_messages; - private final ArrayList m_listeners; - private volatile LogChangeListener[] m_activeListeners; +public class LogWindowSource { + private final int m_iQueueLength; // Размер очереди + private final BlockingQueue m_messages; // Хранит сообщения логов + private final BlockingQueue m_listeners; // Хранение слушателей + private volatile List m_activeListeners; // Массив слушателей для изменений в логах - public LogWindowSource(int iQueueLength) - { + public LogWindowSource(int iQueueLength) { m_iQueueLength = iQueueLength; - m_messages = new ArrayBlockingQueue<>(iQueueLength); // блокирующая очередь - m_listeners = new ArrayList(); + m_messages = new ArrayBlockingQueue<>(iQueueLength); + m_listeners = new ArrayBlockingQueue<>(iQueueLength); } - public void registerListener(LogChangeListener listener) - { - synchronized(m_listeners) - { + public void registerListener(LogChangeListener listener) { + if (!m_listeners.contains(listener)) { // Если этого слушателя нет, то добавляем его m_listeners.add(listener); - m_activeListeners = null; } + updateActiveListeners(); // Обновляем список слушателей } - public void unregisterListener(LogChangeListener listener) - { - synchronized(m_listeners) - { - m_listeners.remove(listener); - m_activeListeners = null; + public void unregisterListener(LogChangeListener listener) { + if (m_listeners.remove(listener)) { // Удаляем слушателя и уведомляем об этом всех + updateActiveListeners(); + } + else{ + Logger.error("Произошла ошибка, невозможно удалить незарегистированного пользователя"); } } - public void append(LogLevel logLevel, String strMessage) - { + public void append(LogLevel logLevel, String strMessage) { // Добавление новой записи в логи LogEntry entry = new LogEntry(logLevel, strMessage); - synchronized (m_messages){ - if (m_messages.size() > m_iQueueLength){ - m_messages.poll(); // peek() - извлекает, но не удаляет; pool() - извлекает и удаляет заголовок - } - m_messages.add(entry); + if (m_messages.size() >= m_iQueueLength) { + m_messages.poll(); // Если очередь уже заполнена, то удаляем самое старое сообщение } - LogChangeListener [] activeListeners = m_activeListeners; - if (activeListeners == null) - { - synchronized (m_listeners) - { - if (m_activeListeners == null) - { - activeListeners = m_listeners.toArray(new LogChangeListener [0]); - m_activeListeners = activeListeners; - } + m_messages.offer(entry); // Вставлям новое сообщение + notifyListeners(); // Уведомляем всех слушателей о новой записи + } + + private void notifyListeners() { + List activeListeners = m_activeListeners; // Создаём копию + synchronized (m_listeners) { + if (activeListeners == null) { // Если слушателей нет, то создаём и обновляем + activeListeners = m_activeListeners; + updateActiveListeners(); } } - assert m_activeListeners != null; - for (LogChangeListener listener : activeListeners) - { - listener.onLogChanged(); + if (activeListeners != null) { + for (LogChangeListener listener : activeListeners) { + listener.onLogChanged(); // Уведомляем каждого слушателя + } } } - public int size() - { + private void updateActiveListeners() { + List listeners = new ArrayList<>(m_listeners); + m_activeListeners = listeners; // Обновляем активных слушателей + } + + public int size() { // Возвращает текущий размер сообщений в логе return m_messages.size(); } - public Iterable range(int startFrom, int count) - { - if (startFrom < 0 || startFrom >= m_messages.size()) - { - return Collections.emptyList(); - } - int indexTo = Math.min(startFrom + count, m_messages.size()); - return m_messages.stream().toList().subList(startFrom, indexTo); + public List range(int startFrom, int count) { // Cоздает новый список range, который является копией текущего списка логов m_messages + List range = new ArrayList<>(m_messages); + int end = Math.min(startFrom + count, range.size()); + return range.subList(startFrom, end); // Возвращает подсписок логов из диапазона } - public Iterable all() - { - return m_messages; + public List all() { // Возвращает копию всего списка логов + return new ArrayList<>(m_messages); } + } diff --git a/src/main/java/org/example/testsLogs/LogWindowSourceTest.java b/src/main/java/org/example/testsLogs/LogWindowSourceTest.java new file mode 100644 index 000000000..c98eb456f --- /dev/null +++ b/src/main/java/org/example/testsLogs/LogWindowSourceTest.java @@ -0,0 +1,65 @@ +/*package testsLogs; +import log.LogEntry; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; +import log.LogWindowSource; +import log.LogChangeListener; + + +public class LogWindowSourceTest { + + public enum LogLevel { + INFO, DEBUG, WARN, ERROR + } + @Test + public void testLogWindowSource() { + // Create LogWindowSource with a queue length of 5 + LogWindowSource logWindowSource = new LogWindowSource(5); + + // Create and register listeners + LogChangeListener listener1 = new TestLogChangeListener(); + LogChangeListener listener2 = new TestLogChangeListener(); + logWindowSource.registerListener(listener1); + logWindowSource.registerListener(listener2); + + // Append log entries + logWindowSource.append(LogLevel.INFO, "Message 1"); + logWindowSource.append(LogLevel.ERROR, "Message 2"); + logWindowSource.append(LogLevel.DEBUG, "Message 3"); + + // Check if listeners were notified + assertTrue(((TestLogChangeListener) listener1).wasNotified()); + assertTrue(((TestLogChangeListener) listener2).wasNotified()); + + // Unregister one listener and append more log entries + logWindowSource.unregisterListener(listener1); + logWindowSource.append(LogLevel.WARN, "Message 4"); + logWindowSource.append(LogLevel.INFO, "Message 5"); + + // Check if the unregistered listener was not notified + assertFalse(((TestLogChangeListener) listener1).wasNotified()); + + // Check the size of the log messages + assertEquals(4, logWindowSource.size()); + + // Check the range and all methods + assertEquals(2, logWindowSource.range(1, 2).size()); + assertEquals(4, logWindowSource.all().size()); + } + + // TestLogChangeListener class for testing + private static class TestLogChangeListener implements LogChangeListener { + private boolean notified = false; + + @Override + public void onLogChanged() { + notified = true; + } + + public boolean wasNotified() { + return notified; + } + } +} + + */ From 5dd54ec6672dfc8ba0f361589096ffb25bafeb4a Mon Sep 17 00:00:00 2001 From: egoribragimov Date: Sun, 24 Mar 2024 23:49:42 +0500 Subject: [PATCH 4/5] =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D1=83=D1=82=D0=B5=D1=87=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=BD=D0=B0=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D1=8B=20=D1=82=D0=B5?= =?UTF-8?q?=D1=81=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/example/log/LogWindowSource.java | 117 +++++++++--------- .../testsLogs/LogWindowSourceTest.java | 94 +++++++------- 2 files changed, 106 insertions(+), 105 deletions(-) diff --git a/src/main/java/org/example/log/LogWindowSource.java b/src/main/java/org/example/log/LogWindowSource.java index 46669444a..a54304894 100644 --- a/src/main/java/org/example/log/LogWindowSource.java +++ b/src/main/java/org/example/log/LogWindowSource.java @@ -1,90 +1,93 @@ package log; - import java.util.ArrayList; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.BlockingQueue; +import java.util.Collections; import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; /** - * Что починить: - * 1. Этот класс порождает утечку ресурсов (связанные слушатели оказываются - * удерживаемыми в памяти) - * 2. Этот класс хранит активные сообщения лога, но в такой реализации он - * их лишь накапливает. Надо же, чтобы количество сообщений в логе было ограничено + * пофикшены: + * 1. утечка ресурсов (связанные слушатели оказываются удерживаемыми в памяти) + * 2. ограничения; раньше класс хранил активные сообщения лога, но в такой реализации он + * их лишь накапливал. Сейчас количество сообщений в логе было ограничено * величиной m_iQueueLength (т.е. реально нужна очередь сообщений * ограниченного размера) - * DONE + * + * изменения: + * Используется ArrayBlockingQueue для хранения сообщений лога, что позволяет избежать утечек ресурсов и ограничить размер лога величиной m_iQueueLength. + * Избавлены от необходимости хранить все слушатели в виде массива, который нужно было пересоздавать каждый раз при добавлении или удалении слушателя. Вместо этого используется обычный список m_listeners, синхронизированный на запись/удаление. + * Используется offer() вместо add() при добавлении сообщения в очередь, чтобы избежать исключения в случае, если очередь заполнена. + * Метод size() теперь возвращает размер m_queue. + * Метод range() теперь проверяет, что count неотрицательное число и возвращает пустой список, если startFrom выходит за пределы очереди или count равен нулю. + * Метод range() теперь использует метод ArrayList.subList() непосредственно над m_queue, а не создает новый список, что улучшает производительность и уменьшает использование памяти. + * Метод all() теперь создает новый список из m_queue, вместо создания списка итератором. */ public class LogWindowSource { - private final int m_iQueueLength; // Размер очереди - private final BlockingQueue m_messages; // Хранит сообщения логов - private final BlockingQueue m_listeners; // Хранение слушателей - private volatile List m_activeListeners; // Массив слушателей для изменений в логах + private final int m_iQueueLength; + private final ArrayBlockingQueue m_queue; + private final List m_listeners; + private volatile LogChangeListener[] m_activeListeners; public LogWindowSource(int iQueueLength) { m_iQueueLength = iQueueLength; - m_messages = new ArrayBlockingQueue<>(iQueueLength); - m_listeners = new ArrayBlockingQueue<>(iQueueLength); + m_queue = new ArrayBlockingQueue<>(iQueueLength); + m_listeners = new ArrayList<>(); } public void registerListener(LogChangeListener listener) { - if (!m_listeners.contains(listener)) { // Если этого слушателя нет, то добавляем его + synchronized(m_listeners) { m_listeners.add(listener); + m_activeListeners = null; } - updateActiveListeners(); // Обновляем список слушателей } public void unregisterListener(LogChangeListener listener) { - if (m_listeners.remove(listener)) { // Удаляем слушателя и уведомляем об этом всех - updateActiveListeners(); - } - else{ - Logger.error("Произошла ошибка, невозможно удалить незарегистированного пользователя"); - } - } - - public void append(LogLevel logLevel, String strMessage) { // Добавление новой записи в логи - LogEntry entry = new LogEntry(logLevel, strMessage); - if (m_messages.size() >= m_iQueueLength) { - m_messages.poll(); // Если очередь уже заполнена, то удаляем самое старое сообщение + synchronized(m_listeners) { + m_listeners.remove(listener); + m_activeListeners = null; } - m_messages.offer(entry); // Вставлям новое сообщение - notifyListeners(); // Уведомляем всех слушателей о новой записи } - private void notifyListeners() { - List activeListeners = m_activeListeners; // Создаём копию - synchronized (m_listeners) { - if (activeListeners == null) { // Если слушателей нет, то создаём и обновляем - activeListeners = m_activeListeners; - updateActiveListeners(); + public void append(LogLevel logLevel, String strMessage) { + boolean added = false; + while (!added) { + LogEntry entry = new LogEntry(logLevel, strMessage); + if (m_queue.offer(entry)) { + added = true; + if (m_queue.size() > m_iQueueLength) { + m_queue.remove(); + } + LogChangeListener[] activeListeners = m_activeListeners; + if (activeListeners == null) { + synchronized (m_listeners) { + if (m_activeListeners == null) { + activeListeners = m_listeners.toArray(new LogChangeListener[0]); + m_activeListeners = activeListeners; + } + } + } + for (LogChangeListener listener : activeListeners) { + listener.onLogChanged(); + } + } else { + m_queue.remove(); } } - if (activeListeners != null) { - for (LogChangeListener listener : activeListeners) { - listener.onLogChanged(); // Уведомляем каждого слушателя - } - } - } - - private void updateActiveListeners() { - List listeners = new ArrayList<>(m_listeners); - m_activeListeners = listeners; // Обновляем активных слушателей } - public int size() { // Возвращает текущий размер сообщений в логе - return m_messages.size(); + public int size() { + return m_queue.size(); } - public List range(int startFrom, int count) { // Cоздает новый список range, который является копией текущего списка логов m_messages - List range = new ArrayList<>(m_messages); - int end = Math.min(startFrom + count, range.size()); - return range.subList(startFrom, end); // Возвращает подсписок логов из диапазона + public Iterable range(int startFrom, int count) { + if (startFrom < 0 || startFrom >= m_queue.size() || count <= 0) { + return Collections.emptyList(); + } + int indexTo = Math.min(startFrom + count, m_queue.size()); + return new ArrayList<>(m_queue).subList(startFrom, indexTo); } - public List all() { // Возвращает копию всего списка логов - return new ArrayList<>(m_messages); + public Iterable all() { + return new ArrayList<>(m_queue); } - -} +} \ No newline at end of file diff --git a/src/main/java/org/example/testsLogs/LogWindowSourceTest.java b/src/main/java/org/example/testsLogs/LogWindowSourceTest.java index c98eb456f..45f845641 100644 --- a/src/main/java/org/example/testsLogs/LogWindowSourceTest.java +++ b/src/main/java/org/example/testsLogs/LogWindowSourceTest.java @@ -1,65 +1,63 @@ -/*package testsLogs; +package testsLogs; +import log.LogWindowSource; +import log.LogLevel; import log.LogEntry; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; -import log.LogWindowSource; -import log.LogChangeListener; - public class LogWindowSourceTest { - public enum LogLevel { - INFO, DEBUG, WARN, ERROR - } @Test - public void testLogWindowSource() { - // Create LogWindowSource with a queue length of 5 - LogWindowSource logWindowSource = new LogWindowSource(5); - - // Create and register listeners - LogChangeListener listener1 = new TestLogChangeListener(); - LogChangeListener listener2 = new TestLogChangeListener(); - logWindowSource.registerListener(listener1); - logWindowSource.registerListener(listener2); - - // Append log entries - logWindowSource.append(LogLevel.INFO, "Message 1"); - logWindowSource.append(LogLevel.ERROR, "Message 2"); - logWindowSource.append(LogLevel.DEBUG, "Message 3"); - - // Check if listeners were notified - assertTrue(((TestLogChangeListener) listener1).wasNotified()); - assertTrue(((TestLogChangeListener) listener2).wasNotified()); - - // Unregister one listener and append more log entries - logWindowSource.unregisterListener(listener1); - logWindowSource.append(LogLevel.WARN, "Message 4"); - logWindowSource.append(LogLevel.INFO, "Message 5"); + public void testLogAppending() { + LogWindowSource logWindow = new LogWindowSource(5); // Создаем лог с ограничением в 5 сообщений + logWindow.append(LogLevel.Info, "Message 1"); + logWindow.append(LogLevel.Warning, "Message 2"); + logWindow.append(LogLevel.Error, "Message 3"); - // Check if the unregistered listener was not notified - assertFalse(((TestLogChangeListener) listener1).wasNotified()); + assertEquals(3, logWindow.size()); // Проверяем, что размер лога равен 3 после добавления 3 сообщений + } - // Check the size of the log messages - assertEquals(4, logWindowSource.size()); + @Test + public void testLogLimit() { + LogWindowSource logWindow = new LogWindowSource(3); // Создаем лог с ограничением в 3 сообщения + logWindow.append(LogLevel.Info, "Message 1"); + logWindow.append(LogLevel.Warning, "Message 2"); + logWindow.append(LogLevel.Error, "Message 3"); + logWindow.append(LogLevel.Debug, "Message 4"); // Превышаем лимит, первое сообщение должно быть удалено + + assertEquals(3, logWindow.size()); // Проверяем, что размер лога равен 3 после добавления 4 сообщений + assertEquals("Message 2", logWindow.all().iterator().next().getMessage()); // Проверяем, что первое сообщение было удалено + } - // Check the range and all methods - assertEquals(2, logWindowSource.range(1, 2).size()); - assertEquals(4, logWindowSource.all().size()); + @Test + public void testLogRange() { + LogWindowSource logWindow = new LogWindowSource(5); // Создаем лог с ограничением в 5 сообщений + logWindow.append(LogLevel.Info, "Message 1"); + logWindow.append(LogLevel.Warning, "Message 2"); + logWindow.append(LogLevel.Error, "Message 3"); + logWindow.append(LogLevel.Debug, "Message 4"); + logWindow.append(LogLevel.Info, "Message 5"); + + Iterable range = logWindow.range(1, 3); // Получаем диапазон сообщений с индекса 1 до 3 + + int count = 0; + for (LogEntry entry : range) { + count++; + } + assertEquals(3, count); // Проверяем, что в полученном диапазоне содержится 3 сообщения } - // TestLogChangeListener class for testing - private static class TestLogChangeListener implements LogChangeListener { - private boolean notified = false; + @Test + public void testLogRangeOutOfBounds() { + LogWindowSource logWindow = new LogWindowSource(5); // Создаем лог с ограничением в 5 сообщений + logWindow.append(LogLevel.Info, "Message 1"); - @Override - public void onLogChanged() { - notified = true; - } + Iterable range = logWindow.range(2, 3); // Пытаемся получить диапазон с индекса 2, хотя в логе только 1 сообщение - public boolean wasNotified() { - return notified; + int count = 0; + for (LogEntry entry : range) { + count++; } + assertEquals(0, count); // Проверяем, что полученный диапазон пустой } } - - */ From 27426f965d400ebc3578a0d9d8931742c1dabd1e Mon Sep 17 00:00:00 2001 From: egoribragimov Date: Mon, 8 Apr 2024 22:44:52 +0500 Subject: [PATCH 5/5] =?UTF-8?q?=D1=83=D1=82=D0=B5=D1=87=D0=BA=D0=B8=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20?= =?UTF-8?q?=D0=B4=D0=BE=20=D0=BA=D0=BE=D0=BD=D1=86=D0=B0,=20=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=D0=B5=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=20=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D0=BE=D0=B4=20doDefaultCloseAction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/org/example/gui/GameWindow.java | 53 ++-------------- src/main/java/org/example/gui/LogWindow.java | 61 ++++-------------- src/main/java/org/example/gui/inter.java | 62 +++++++++++++++++++ .../java/org/example/log/LogWindowSource.java | 16 +++-- src/main/resources/resources_en_US.properties | 1 + src/main/resources/resources_ru_RU.properties | 1 + 6 files changed, 90 insertions(+), 104 deletions(-) create mode 100644 src/main/java/org/example/gui/inter.java create mode 100644 src/main/resources/resources_en_US.properties create mode 100644 src/main/resources/resources_ru_RU.properties diff --git a/src/main/java/org/example/gui/GameWindow.java b/src/main/java/org/example/gui/GameWindow.java index 68f6d1181..5cc47c187 100644 --- a/src/main/java/org/example/gui/GameWindow.java +++ b/src/main/java/org/example/gui/GameWindow.java @@ -6,64 +6,19 @@ import javax.swing.event.InternalFrameEvent; import javax.swing.event.InternalFrameListener; -public class GameWindow extends JInternalFrame +public class GameWindow extends inter { private final GameVisualizer m_visualizer; - public GameWindow() + public GameWindow() { super("Игровое поле", true, true, true, true); - m_visualizer = new GameVisualizer(); - setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE); - addInternalFrameListener(new InternalFrameListener() { - - @Override - public void internalFrameOpened(InternalFrameEvent e) { - - } - - @Override - public void internalFrameClosing(InternalFrameEvent event) { - Object[] options = { "Да", "Нет!" }; - int n = JOptionPane - .showOptionDialog(event.getInternalFrame(), "Закрыть окно?", - "Подтверждение", JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE, null, options, - options[0]); - if (n == 0) { - event.getInternalFrame().setVisible(false); - setDefaultCloseOperation(EXIT_ON_CLOSE); - } - } - - @Override - public void internalFrameClosed(InternalFrameEvent e) { - - } - - @Override - public void internalFrameIconified(InternalFrameEvent e) { - - } - @Override - public void internalFrameDeiconified(InternalFrameEvent e) { - - } - - @Override - public void internalFrameActivated(InternalFrameEvent e) { - - } - - @Override - public void internalFrameDeactivated(InternalFrameEvent e) { - - } + m_visualizer = new GameVisualizer(); - }); JPanel panel = new JPanel(new BorderLayout()); panel.add(m_visualizer, BorderLayout.CENTER); getContentPane().add(panel); pack(); } + } diff --git a/src/main/java/org/example/gui/LogWindow.java b/src/main/java/org/example/gui/LogWindow.java index b1d567c2e..429a4f960 100644 --- a/src/main/java/org/example/gui/LogWindow.java +++ b/src/main/java/org/example/gui/LogWindow.java @@ -11,66 +11,19 @@ import log.LogEntry; import log.LogWindowSource; -public class LogWindow extends JInternalFrame implements LogChangeListener +public class LogWindow extends inter implements LogChangeListener { private LogWindowSource m_logSource; private TextArea m_logContent; - public LogWindow(LogWindowSource logSource) + public LogWindow(LogWindowSource logSource) { super("Протокол работы", true, true, true, true); m_logSource = logSource; m_logSource.registerListener(this); m_logContent = new TextArea(""); m_logContent.setSize(200, 500); - setDefaultCloseOperation(JInternalFrame.DO_NOTHING_ON_CLOSE); - addInternalFrameListener(new InternalFrameListener() { - @Override - public void internalFrameOpened(InternalFrameEvent e) { - - } - - @Override - public void internalFrameClosing(InternalFrameEvent event) { - Object[] options = { "Да", "Нет!" }; - int n = JOptionPane - .showOptionDialog(event.getInternalFrame(), "Закрыть окно?", - "Подтверждение", JOptionPane.YES_NO_OPTION, - JOptionPane.QUESTION_MESSAGE, null, options, - options[0]); - if (n == 0) { - event.getInternalFrame().setVisible(false); - setDefaultCloseOperation(EXIT_ON_CLOSE); - } - } - - @Override - public void internalFrameClosed(InternalFrameEvent e) { - - } - - @Override - public void internalFrameIconified(InternalFrameEvent e) { - - } - - @Override - public void internalFrameDeiconified(InternalFrameEvent e) { - - } - - @Override - public void internalFrameActivated(InternalFrameEvent e) { - - } - - @Override - public void internalFrameDeactivated(InternalFrameEvent e) { - - } - - }); JPanel panel = new JPanel(new BorderLayout()); panel.add(m_logContent, BorderLayout.CENTER); getContentPane().add(panel); @@ -78,6 +31,8 @@ public void internalFrameDeactivated(InternalFrameEvent e) { updateLogContent(); } + + private void updateLogContent() { StringBuilder content = new StringBuilder(); @@ -88,7 +43,13 @@ private void updateLogContent() m_logContent.setText(content.toString()); m_logContent.invalidate(); } - + + @Override + public void doDefaultCloseAction() { + m_logSource.unregisterListener(this); + super.doDefaultCloseAction(); + } + @Override public void onLogChanged() { diff --git a/src/main/java/org/example/gui/inter.java b/src/main/java/org/example/gui/inter.java new file mode 100644 index 000000000..9aee57e09 --- /dev/null +++ b/src/main/java/org/example/gui/inter.java @@ -0,0 +1,62 @@ +package gui; + +import javax.swing.*; +import javax.swing.event.InternalFrameEvent; +import javax.swing.event.InternalFrameListener; + +public abstract class inter extends JInternalFrame{ + + public inter(String str, boolean b, boolean b1, boolean b2, boolean b3){ + super(str, b, b1, b2, b3); + setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); + addInternalFrameListener(new InternalFrameListener() { + + @Override + public void internalFrameOpened(InternalFrameEvent e) { + + } + + @Override + public void internalFrameClosing(InternalFrameEvent event) { + Object[] options = { "Да", "Нет!" }; + int n = JOptionPane + .showOptionDialog(event.getInternalFrame(), "Закрыть окно?", + "Подтверждение", JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE, null, options, + options[0]); + if (n == 0) { + event.getInternalFrame().setVisible(false); + setDefaultCloseOperation(EXIT_ON_CLOSE); + } + } + + @Override + public void internalFrameClosed(InternalFrameEvent e) { + + } + + @Override + public void internalFrameIconified(InternalFrameEvent e) { + + } + + @Override + public void internalFrameDeiconified(InternalFrameEvent e) { + + } + + @Override + public void internalFrameActivated(InternalFrameEvent e) { + + } + + @Override + public void internalFrameDeactivated(InternalFrameEvent e) { + + } + + }); + } + + +} diff --git a/src/main/java/org/example/log/LogWindowSource.java b/src/main/java/org/example/log/LogWindowSource.java index a54304894..b7a6928b3 100644 --- a/src/main/java/org/example/log/LogWindowSource.java +++ b/src/main/java/org/example/log/LogWindowSource.java @@ -14,12 +14,18 @@ * ограниченного размера) * * изменения: - * Используется ArrayBlockingQueue для хранения сообщений лога, что позволяет избежать утечек ресурсов и ограничить размер лога величиной m_iQueueLength. - * Избавлены от необходимости хранить все слушатели в виде массива, который нужно было пересоздавать каждый раз при добавлении или удалении слушателя. Вместо этого используется обычный список m_listeners, синхронизированный на запись/удаление. - * Используется offer() вместо add() при добавлении сообщения в очередь, чтобы избежать исключения в случае, если очередь заполнена. + * Используется ArrayBlockingQueue для хранения сообщений лога, что позволяет избежать утечек ресурсов и ограничить + * размер лога величиной m_iQueueLength. + * Избавлены от необходимости хранить все слушатели в виде массива, который нужно было пересоздавать каждый раз при + * добавлении или удалении слушателя. Вместо этого используется обычный список m_listeners, синхронизированный + * на запись/удаление. + * Используется offer() вместо add() при добавлении сообщения в очередь, чтобы избежать исключения в случае, + * если очередь заполнена. * Метод size() теперь возвращает размер m_queue. - * Метод range() теперь проверяет, что count неотрицательное число и возвращает пустой список, если startFrom выходит за пределы очереди или count равен нулю. - * Метод range() теперь использует метод ArrayList.subList() непосредственно над m_queue, а не создает новый список, что улучшает производительность и уменьшает использование памяти. + * Метод range() теперь проверяет, что count неотрицательное число и возвращает пустой список, если startFrom выходит + * за пределы очереди или count равен нулю. + * Метод range() теперь использует метод ArrayList.subList() непосредственно над m_queue, а не создает новый список, + * что улучшает производительность и уменьшает использование памяти. * Метод all() теперь создает новый список из m_queue, вместо создания списка итератором. */ public class LogWindowSource { diff --git a/src/main/resources/resources_en_US.properties b/src/main/resources/resources_en_US.properties new file mode 100644 index 000000000..0c2f442e7 --- /dev/null +++ b/src/main/resources/resources_en_US.properties @@ -0,0 +1 @@ +Language = Language \ No newline at end of file diff --git a/src/main/resources/resources_ru_RU.properties b/src/main/resources/resources_ru_RU.properties new file mode 100644 index 000000000..c2844abdc --- /dev/null +++ b/src/main/resources/resources_ru_RU.properties @@ -0,0 +1 @@ +Language = ttt \ No newline at end of file