From a6755c28b36b3ca269baee19d72b7819df4c0b0f Mon Sep 17 00:00:00 2001 From: admin Date: Sun, 3 Mar 2024 06:54:19 +1000 Subject: [PATCH] Introduces a stylesHash in order to not waste time updating lines that already match the required styles. --- src/urChatBasic/base/IRCChannelBase.java | 4 +- src/urChatBasic/frontend/LineFormatter.java | 118 +++++++++++++++--- src/urChatBasic/frontend/UserGUI.java | 2 +- .../frontend/panels/ConnectionPanel.java | 1 + tests/frontend/AppearanceTests.java | 30 +++-- tests/frontend/LAFTests.java | 10 +- tests/frontend/LineFormatterTests.java | 2 +- .../frontend/UpdateStylesBenchmarkTests.java | 55 +++++++- 8 files changed, 190 insertions(+), 32 deletions(-) diff --git a/src/urChatBasic/base/IRCChannelBase.java b/src/urChatBasic/base/IRCChannelBase.java index 969d3d7..de0dc9c 100644 --- a/src/urChatBasic/base/IRCChannelBase.java +++ b/src/urChatBasic/base/IRCChannelBase.java @@ -188,14 +188,14 @@ private void initChannel() fontDialog = new FontDialog(channelName, gui.getStyle(), channelPrefs); - lineFormatter = new LineFormatter(getFontPanel().getStyle(), channelTextArea , getServer(), channelPrefs); + lineFormatter = new LineFormatter(getFontPanel().getStyle(), channelTextArea , channelScroll, getServer(), channelPrefs); } else { markerName = channelName; setSettingsPath(URProfilesUtil.getActiveFavouritesPath().node(channelName)); fontDialog = new FontDialog(channelName, gui.getStyle(), channelPrefs); - lineFormatter = new LineFormatter(getFontPanel().getStyle() , channelTextArea, null, channelPrefs); + lineFormatter = new LineFormatter(getFontPanel().getStyle() , channelTextArea, channelScroll, null, channelPrefs); } // Add Logging Marker diff --git a/src/urChatBasic/frontend/LineFormatter.java b/src/urChatBasic/frontend/LineFormatter.java index 4577f69..d3503f3 100644 --- a/src/urChatBasic/frontend/LineFormatter.java +++ b/src/urChatBasic/frontend/LineFormatter.java @@ -2,7 +2,9 @@ import java.awt.Color; import java.awt.Desktop; +import java.awt.Dimension; import java.awt.Font; +import java.awt.Point; import java.awt.event.ActionEvent; import java.net.URL; import java.time.Duration; @@ -22,7 +24,9 @@ import javax.swing.AbstractAction; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; import javax.swing.JTextPane; +import javax.swing.JViewport; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.text.AttributeSet; @@ -56,6 +60,7 @@ public class LineFormatter // private URStyle mediumStyle; // private URStyle lowStyle; private JTextPane docOwner; + private JScrollPane docScroller; public StyledDocument doc; // public URStyle myStyle; private Map formatterStyles = new HashMap<>(); @@ -64,12 +69,13 @@ public class LineFormatter private AtomicLong updateStylesTime = new AtomicLong(0); public AtomicBoolean updateStylesInProgress = new AtomicBoolean(false); - public LineFormatter(URStyle baseStyle, JTextPane docOwner ,final IRCServerBase server, Preferences settingsPath) + public LineFormatter(URStyle baseStyle, JTextPane docOwner ,JScrollPane docScroller, final IRCServerBase server, Preferences settingsPath) { // TODO: Need to load attributes from formatterPrefs this.settingsPath = settingsPath; this.docOwner = docOwner; + this.docScroller = docScroller; // this.docOwner.setBackground(UIManager.getColor(Constants.DEFAULT_BACKGROUND_STRING)); doc = this.docOwner.getStyledDocument(); @@ -144,6 +150,42 @@ public void initStyles (URStyle baseStyle) } } + public int getStylesHash () + { + int formatterStylesHash = formatterStyles.hashCode(); + int nickFormatHash = String.join("", DriverGUI.gui.getNickFormatString("nick")).hashCode(); + int timeStampHash = DriverGUI.gui.getTimeStampString(new Date(0L)).hashCode(); + + + return formatterStylesHash + nickFormatHash + timeStampHash; + } + + class ViewPortRange { + private int start; + private int end; + + public ViewPortRange () + { + JViewport viewport = docScroller.getViewport(); + Point startPoint = viewport.getViewPosition(); + Dimension size = viewport.getExtentSize(); + Point endPoint = new Point(startPoint.x + size.width, startPoint.y + size.height); + + start = docOwner.viewToModel2D(startPoint); + end = docOwner.viewToModel2D(endPoint); + } + + public int getStart () + { + return start; + } + + public int getEnd () + { + return end; + } + } + public URStyle dateStyle(URStyle baseStyle, Date date, boolean load) { // TODO: date style can only be lowStyle or defaultStyle @@ -539,8 +581,14 @@ public URStyle getStyle(String styleName, boolean load) */ public void updateStyles (URStyle newBaseStyle) { + int lastStylesHash = getStylesHash(); + + updateStylesInProgress.set(true); + initStyles(newBaseStyle); + int currentStylesHash = getStylesHash(); + if (doc.getLength() > 0) { SwingUtilities.invokeLater(new Runnable() @@ -549,13 +597,12 @@ public void run () { try { - if(!updateStylesInProgress.get()) + if (currentStylesHash != lastStylesHash) { Constants.LOGGER.info( "Updating styles for " + settingsPath.name()); - updateStylesTime.set(Instant.now().getEpochSecond()); - updateDocStyles(0); + updateDocStyles(0, getStylesHash()); } else { - Constants.LOGGER.info( "Update already in progress."); + Constants.LOGGER.debug("NOT updating styles for " + settingsPath.name()+". Styles hash matches."); } } catch (BadLocationException e) { @@ -568,9 +615,9 @@ public void run () } } - private void updateDocStyles (int currentPosition) throws BadLocationException + private void updateDocStyles (int currentPosition, int updateHash) throws BadLocationException { - updateStylesInProgress.set(true); + updateStylesTime.set(Instant.now().getEpochSecond()); Element root = doc.getDefaultRootElement(); int lineCount = root.getElementCount(); int lineIndex = 0; @@ -593,6 +640,10 @@ private void updateDocStyles (int currentPosition) throws BadLocationException // Has style to update if (currentStyle != null && currentStyle.getAttributeCount() > 0) { + // this line has already been updated + if(currentStyle.getAttribute("stylesHash") != null && currentStyle.getAttribute("stylesHash").equals(updateHash)) + break; + int styleLength = Integer.parseInt(currentStyle.getAttribute("styleLength").toString()); String styleString = doc.getText(currentPosition, styleLength); @@ -610,8 +661,10 @@ private void updateDocStyles (int currentPosition) throws BadLocationException doc.remove(currentPosition, styleLength); currentStyle = dateStyle(currentStyle, lineDate, false); // Inserts the new timestamp, and updates the formatting + currentStyle.addAttribute("stylesHash", getStylesHash()); insertString(newTimeStamp, currentStyle, currentPosition); } else { + currentStyle.addAttribute("stylesHash", getStylesHash()); setDocAttributes(currentPosition, styleLength, currentStyle); } } else @@ -626,6 +679,7 @@ private void updateDocStyles (int currentPosition) throws BadLocationException currentStyle = dateStyle(currentStyle, lineDate, false); // Inserts the new string, and updates the formatting + currentStyle.addAttribute("stylesHash", getStylesHash()); insertString(newTimeStamp, currentStyle, currentPosition); } } @@ -773,6 +827,33 @@ public String getLatestLine() throws BadLocationException return finalLine; } + public String getLine(int lineNumber) throws BadLocationException + { + Element root = doc.getDefaultRootElement(); + int lines = root.getElementCount(); + + String finalLine = ""; + + while (finalLine.isEmpty()) + { + + if (lines < 0) + break; + + Element line = root.getElement(lineNumber); + + if (null == line) + continue; + + int start = line.getStartOffset(); + int end = line.getEndOffset(); + String text = doc.getText(start, end - start); + finalLine = text.trim(); + } + + return finalLine; + } + // New method to get line at position public String getLineAtPosition(int position) throws BadLocationException { Element root = doc.getDefaultRootElement(); @@ -798,29 +879,32 @@ public int getLineCount () return root.getElementCount(); } - private int getLinePosition(String targetLine) throws BadLocationException - { + private int getLinePosition(String targetLine) throws BadLocationException { Element root = doc.getDefaultRootElement(); int lines = root.getElementCount(); - for (int i = 0; i < lines; i++) - { + // Create a regex pattern to match the target line + Pattern pattern = Pattern.compile(".*"+Pattern.quote(targetLine.trim())+"$"); + + for (int i = 0; i < lines; i++) { Element line = root.getElement(i); - if (null == line) + if (line == null) continue; int start = line.getStartOffset(); int end = line.getEndOffset(); String text = doc.getText(start, end - start); - if (text.trim().equals(targetLine.trim())) - { + Matcher matcher = pattern.matcher(text.trim()); + + // If the pattern matches, return the start offset of the line + if (matcher.find()) { return start; } } - return 0; + return -1; // Return -1 if the target line is not found } public URStyle getStyleAtPosition(int position, String relativeLine) @@ -967,7 +1051,9 @@ public void appendMessage(Optional lineDate, IRCUser fromUser, String from { // add the date to the end of the string to preserve the timestamp of the line // when updating styles - insertPosition = addString(DriverGUI.gui.getTimeStampString(timeLine.get()) + " ", dateStyle(timePositionStyle, lineDate.get(), false), Optional.of(insertPosition)); + URStyle lineDateStyle = dateStyle(timePositionStyle, lineDate.get(), false); + lineDateStyle.addAttribute("stylesHash", getStylesHash()); + insertPosition = addString(DriverGUI.gui.getTimeStampString(timeLine.get()) + " ", lineDateStyle, Optional.of(insertPosition)); } linePositionStyle.addAttribute("type", "nickPart0"); diff --git a/src/urChatBasic/frontend/UserGUI.java b/src/urChatBasic/frontend/UserGUI.java index c00e869..d71dd5c 100644 --- a/src/urChatBasic/frontend/UserGUI.java +++ b/src/urChatBasic/frontend/UserGUI.java @@ -334,7 +334,7 @@ public void updatePreviewTextArea () // previewTextArea.setFont(clientFontPanel.getFont()); if (previewLineFormatter == null) { - previewLineFormatter = new LineFormatter(clientFontPanel.getStyle(), previewTextArea, null, URProfilesUtil.getActiveProfilePath()); + previewLineFormatter = new LineFormatter(clientFontPanel.getStyle(), previewTextArea, previewTextScroll, null, URProfilesUtil.getActiveProfilePath()); URProfilesUtil.addListener(EventType.CHANGE, e -> { previewLineFormatter.setSettingsPath(URProfilesUtil.getActiveProfilePath()); diff --git a/src/urChatBasic/frontend/panels/ConnectionPanel.java b/src/urChatBasic/frontend/panels/ConnectionPanel.java index a61c807..02c6139 100644 --- a/src/urChatBasic/frontend/panels/ConnectionPanel.java +++ b/src/urChatBasic/frontend/panels/ConnectionPanel.java @@ -243,6 +243,7 @@ public String getChannelName () public void createPopUp () { myMenu = new FavouritesPopUp(); + myMenu.setUI(new JPopupMenu().getUI()); } private class FavouritesPopUp extends JPopupMenu diff --git a/tests/frontend/AppearanceTests.java b/tests/frontend/AppearanceTests.java index 1f8fe63..feaf75b 100644 --- a/tests/frontend/AppearanceTests.java +++ b/tests/frontend/AppearanceTests.java @@ -8,6 +8,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; +import java.util.concurrent.TimeUnit; import javax.swing.text.BadLocationException; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; @@ -34,7 +35,7 @@ public class AppearanceTests IRCServer testServer; TestDriverGUI testDriver; UserGUI testGUI; - final static int MAX_CHANNEL_NAMES = 10; + final static int MAX_CHANNEL_NAMES = 1; final static String CHANNEL_PREFIX = "#someChannel"; final List PUB_CHANNEL_NAMES = new ArrayList<>(); IRCUser testUser; @@ -100,13 +101,21 @@ public void changeDefaultFontAndSizeTest () throws BadLocationException, Interru { IRCChannel pubChannel = testServer.getCreatedChannel(pubChannelName); log("Have joined " + pubChannelName + " successfully?", true); - String welcomeMessage = pubChannel.getLineFormatter().getLineAtPosition(13).split("] ")[1].trim(); + String welcomeMessage = String.join("",testGUI.getNickFormatString("someuser")) + " Welcome to " + pubChannelName; assertEquals(" Welcome to " + pubChannelName, welcomeMessage); - log("Check current style in the channel is correct.", true); + log("Wait for styles to update correctly..", true); TestDriverGUI.waitForEverything(testGUI); - URStyle channelStyle = pubChannel.getLineFormatter().getStyleAtPosition(22, welcomeMessage); + URStyle channelStyle = null; + + while (channelStyle == null || !channelStyle.equals(guiStyle)) + { + TimeUnit.MILLISECONDS.sleep(10); + channelStyle = pubChannel.getLineFormatter().getStyleAtPosition(22, welcomeMessage); + } + + log("Check current style in the channel is correct.", true); assertTrue(guiStyle.equals(channelStyle)); } @@ -126,10 +135,17 @@ public void changeDefaultFontAndSizeTest () throws BadLocationException, Interru TestDriverGUI.waitForEverything(testGUI); IRCChannel pubChannel = testServer.getCreatedChannel(pubChannelName); - String welcomeMessage = pubChannel.getLineFormatter().getLineAtPosition(13).split("] ")[1].trim(); - log("Check current style has updated.", true); + String welcomeMessage = pubChannel.getLineFormatter().getLineAtPosition(22).split("] ")[1].trim(); + log("Wait for current style has updated.", true); - URStyle channelStyle = pubChannel.getLineFormatter().getStyleAtPosition(22, welcomeMessage); + URStyle channelStyle = null; + + while (channelStyle == null || !channelStyle.equals(newStyle)) + { + TimeUnit.MILLISECONDS.sleep(10); + channelStyle = pubChannel.getLineFormatter().getStyleAtPosition(22, welcomeMessage); + channelStyle.removeAttribute("name"); + } log("Test Style: " + guiStyle, true); log("Channel Style: " + channelStyle, true); diff --git a/tests/frontend/LAFTests.java b/tests/frontend/LAFTests.java index e9cfda1..aca1623 100644 --- a/tests/frontend/LAFTests.java +++ b/tests/frontend/LAFTests.java @@ -2,6 +2,7 @@ import static org.testng.AssertJUnit.*; import java.awt.Color; +import java.util.concurrent.TimeUnit; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.text.StyleConstants; @@ -69,7 +70,14 @@ public void changingLAFUpdatesPreviewStyle() throws Exception TestDriverGUI.waitForEverything(gui); - Color newBackgroundColor = (Color) gui.previewLineFormatter.getStyleAtPosition(0, "urChat has loaded - this is an Event").getAttribute(StyleConstants.Background); + Color newBackgroundColor = null; + + while (newBackgroundColor == null || !newBackgroundColor.equals(UIManager.getColor(Constants.DEFAULT_BACKGROUND_STRING))) + { + TimeUnit.MILLISECONDS.sleep(10); + newBackgroundColor = (Color) gui.previewLineFormatter.getStyleAtPosition(0, "urChat has loaded - this is an Event").getAttribute(StyleConstants.Background); + } + Color lineFormatterBackground = gui.previewTextArea.getBackground(); diff --git a/tests/frontend/LineFormatterTests.java b/tests/frontend/LineFormatterTests.java index 4c418dd..ed4a08f 100644 --- a/tests/frontend/LineFormatterTests.java +++ b/tests/frontend/LineFormatterTests.java @@ -103,7 +103,7 @@ public void run() while(!canContinue.get()) { - TimeUnit.SECONDS.sleep(1); + TimeUnit.MILLISECONDS.sleep(10); } // Right-Click mouse event at the x-y coords of the caret in the text pane diff --git a/tests/frontend/UpdateStylesBenchmarkTests.java b/tests/frontend/UpdateStylesBenchmarkTests.java index 709b9d1..34b6c6c 100644 --- a/tests/frontend/UpdateStylesBenchmarkTests.java +++ b/tests/frontend/UpdateStylesBenchmarkTests.java @@ -1,14 +1,19 @@ package frontend; +import static org.junit.Assert.assertEquals; import static org.testng.Reporter.log; import java.awt.Font; import java.awt.GraphicsEnvironment; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Random; +import java.util.concurrent.TimeUnit; +import javax.swing.text.BadLocationException; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; @@ -21,6 +26,7 @@ import urChatBasic.base.capabilities.CapabilityTypes; import urChatBasic.base.proxy.ProxyTypes; import urChatBasic.frontend.DriverGUI; +import urChatBasic.frontend.IRCChannel; import urChatBasic.frontend.IRCServer; import urChatBasic.frontend.IRCUser; import urChatBasic.frontend.UserGUI; @@ -33,7 +39,7 @@ public class UpdateStylesBenchmarkTests { IRCServer testServer; TestDriverGUI testDriver; UserGUI testGUI; - final static int MAX_CHANNEL_NAMES = 10; + final static int MAX_CHANNEL_NAMES = 1; final static String CHANNEL_PREFIX = "#someChannel"; final List PUB_CHANNEL_NAMES = new ArrayList<>(); IRCUser testUser; @@ -82,7 +88,6 @@ public void setUp () throws Exception private void logImportantInfo (String message) { importantInfo.add(message); - log(message, true); } @AfterClass(alwaysRun = true) @@ -117,7 +122,7 @@ public void changeFontBenchmark () throws InterruptedException } @Test - public void changeColoursBenchmark () throws InterruptedException + public void changeColoursBenchmark () throws InterruptedException, BadLocationException { Instant startBenchmark = Instant.now(); // Get Current Font in Appearance panel @@ -128,9 +133,51 @@ public void changeColoursBenchmark () throws InterruptedException log("Set foreground to " +URColour.hexEncode(newStyle.getForeground().get()), true); testGUI.getFontPanel().setDefaultStyle(newStyle); - TestDriverGUI.waitForEverything(testGUI); + guiStyle = testGUI.getStyle(); logImportantInfo( "Took " + Duration.between(startBenchmark, Instant.now()).toMillis() + "ms to update colours."); + + assertEquals(newStyle, guiStyle); + + for (String pubChannelName : PUB_CHANNEL_NAMES) + { + TestDriverGUI.waitForEverything(testGUI); + + IRCChannel pubChannel = testServer.getCreatedChannel(pubChannelName); + // String welcomeMessage = pubChannel.getLineFormatter().getLineAtPosition(13).split("] ")[1].trim(; + String welcomeMessage = String.join("",testGUI.getNickFormatString("someuser")) + " Welcome to " + pubChannelName; + log("Check current style has updated.", true); + + URStyle channelStyle = null; + + while (channelStyle == null || !channelStyle.equals(newStyle)) + { + TimeUnit.MILLISECONDS.sleep(10); + channelStyle = pubChannel.getLineFormatter().getStyleAtPosition(22, welcomeMessage); + } + + log("Test Style: " + guiStyle, true); + log("Channel Style: " + channelStyle, true); + + String testStyleFont = guiStyle.getFamily().get(); + String channelStyleFont = channelStyle.getFamily().get(); + log("Checking "+pubChannelName+" formatting...", true); + + assertEquals(pubChannelName + " font family doesn't match GUI font family.", testStyleFont, channelStyleFont); + + int testStyleSize = guiStyle.getSize().get(); + int channelStyleSize = channelStyle.getSize().get(); + assertEquals(pubChannelName + " font size doesn't match GUI font size.", testStyleSize, channelStyleSize); + + String testStyleForeground = URColour.hexEncode(guiStyle.getForeground().get()); + String channelStyleForeground = URColour.hexEncode(channelStyle.getForeground().get()); + assertEquals(pubChannelName + " foreground doesn't match GUI font foreground.", testStyleForeground, channelStyleForeground); + + String testStyleBackground = URColour.hexEncode(guiStyle.getBackground().get()); + String channelStyleBackground = URColour.hexEncode(channelStyle.getBackground().get()); + assertEquals(pubChannelName + " background doesn't match GUI font background.", testStyleBackground, channelStyleBackground); + + } } @Test