Skip to content

Commit

Permalink
Add a Menu allowing the user to reopen the logs with a different enco…
Browse files Browse the repository at this point in the history
…ding
  • Loading branch information
tibagni committed Mar 9, 2024
1 parent dfeb9d5 commit c62448f
Show file tree
Hide file tree
Showing 11 changed files with 91 additions and 30 deletions.
3 changes: 3 additions & 0 deletions src/main/java/com/tibagni/logviewer/LogViewerPresenter.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.tibagni.logviewer.log.LogStream;

import java.io.File;
import java.nio.charset.Charset;
import java.util.List;

public interface LogViewerPresenter {
Expand All @@ -30,7 +31,9 @@ enum UserSelection {
void saveFilters(String group);
void loadFilters(File[] filtersFile, boolean keepCurrentFilters);
void loadLogs(File[] logFiles);
void loadLogs(File[] logFiles, Charset charset);
void refreshLogs();
void refreshLogsWithDifferentCharset(Charset charset);
void saveFilteredLogs(File file);
void applyFilters();
void filterEdited(Filter filter);
Expand Down
20 changes: 18 additions & 2 deletions src/main/java/com/tibagni/logviewer/LogViewerPresenterImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import org.jetbrains.annotations.NotNull;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
Expand Down Expand Up @@ -394,12 +396,17 @@ public void loadFilters(File[] filtersFiles, boolean keepCurrentFilters) {
}

@Override
public void loadLogs(File[] logFiles) {
public void loadLogs(File[] logFiles) {
loadLogs(logFiles, StandardCharsets.UTF_8);
}

@Override
public void loadLogs(File[] logFiles, Charset charset) {
// Clean up the filters info as it does not apply anymore
cleanUpFilterTempInfo();
doAsync(() -> {
try {
logsRepository.openLogFiles(logFiles, this::updateAsyncProgress);
logsRepository.openLogFiles(logFiles, charset, this::updateAsyncProgress);
rebuildLogStreamsMap(logsRepository.getAvailableStreams());
filteredLogs.clear();
cachedAllowedFilteredLogs.clear();
Expand Down Expand Up @@ -450,6 +457,15 @@ public void loadLogs(File[] logFiles) {
});
}

@Override
public void refreshLogsWithDifferentCharset(Charset charset) {
if (logsRepository.getCurrentlyOpenedLogFiles().isEmpty()) {
view.showErrorMessage("No logs currently open");
} else {
loadLogs(logsRepository.getCurrentlyOpenedLogFiles().toArray(new File[0]), charset);
}
}

@Override
public void refreshLogs() {
if (logsRepository.getCurrentlyOpenedLogFiles().isEmpty()) {
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/tibagni/logviewer/LogViewerView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import com.tibagni.logviewer.view.*
import java.awt.*
import java.awt.event.*
import java.io.File
import java.nio.charset.Charset
import javax.swing.*
import kotlin.collections.HashMap
import kotlin.collections.HashSet
Expand All @@ -27,6 +28,7 @@ interface LogViewerView : View {
fun buildStreamsMenu(): JMenu?
fun handleOpenLogsMenu()
fun handleRefreshLogsMenu()
fun handleChangeCharsetMenu(charset: Charset)
fun handleSaveFilteredLogsMenu()
fun handleOpenFiltersMenu()
fun handleGoToTimestampMenu()
Expand Down Expand Up @@ -570,6 +572,7 @@ class LogViewerViewImpl(private val mainView: MainView, initialLogFiles: Set<Fil
}

override fun handleRefreshLogsMenu() = presenter.refreshLogs()
override fun handleChangeCharsetMenu(charset: Charset) = presenter.refreshLogsWithDifferentCharset(charset)

override fun handleSaveFilteredLogsMenu() {
val file = mainView.showSaveLogFileChooser()
Expand Down
8 changes: 5 additions & 3 deletions src/main/java/com/tibagni/logviewer/LogsRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import com.tibagni.logviewer.log.*
import com.tibagni.logviewer.log.parser.LogParser
import com.tibagni.logviewer.logger.wrapProfiler
import java.io.File
import java.nio.charset.Charset
import java.nio.charset.StandardCharsets
import java.util.*

class OpenLogsException(message: String?, cause: Throwable) : java.lang.Exception(message, cause)
Expand All @@ -24,7 +26,7 @@ interface LogsRepository {
val allLogsSize: Int

@Throws(OpenLogsException::class)
fun openLogFiles(files: Array<File>, progressReporter: ProgressReporter)
fun openLogFiles(files: Array<File>, charset: Charset, progressReporter: ProgressReporter)
fun getMatchingLogEntry(entry: LogEntry): LogEntry?
}

Expand Down Expand Up @@ -67,10 +69,10 @@ class LogsRepositoryImpl : LogsRepository {


@Throws(OpenLogsException::class)
override fun openLogFiles(files: Array<File>, progressReporter: ProgressReporter) {
override fun openLogFiles(files: Array<File>, charset: Charset, progressReporter: ProgressReporter) {
try {
val logParser = LogParser(FileLogReader(files), progressReporter)
val parsedLogs = wrapProfiler("ParseLogs") { logParser.parseLogs() }
val parsedLogs = wrapProfiler("ParseLogs") { logParser.parseLogs(charset) }

_firstVisibleLogIndex = 0
_lastVisibleLogIndex = parsedLogs.lastIndex
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/com/tibagni/logviewer/MainView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import java.io.File
import java.io.IOException
import java.net.URISyntaxException
import java.net.URL
import java.nio.charset.StandardCharsets
import javax.swing.*
import javax.swing.filechooser.FileNameExtensionFilter

Expand Down Expand Up @@ -261,6 +262,11 @@ class MainViewImpl(
refreshLogsItem.accelerator = KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0)
refreshLogsItem.addActionListener { logViewerView.handleRefreshLogsMenu() }
logsMenu.add(refreshLogsItem)

val changeEncodingMenu = JMenu("Change encoding")
configureCharsetsMenu(changeEncodingMenu)
logsMenu.add(changeEncodingMenu)

saveFilteredLogs = JMenuItem("Save Filtered Logs")
saveFilteredLogs?.addActionListener { logViewerView.handleSaveFilteredLogsMenu() }
logsMenu.add(saveFilteredLogs)
Expand Down Expand Up @@ -304,6 +310,27 @@ class MainViewImpl(
menuBar.repaint()
}

private fun configureCharsetsMenu(menu: JMenu) {
val asciiSubMenuItem = JMenuItem("ASCII")
asciiSubMenuItem.addActionListener { logViewerView.handleChangeCharsetMenu(StandardCharsets.US_ASCII) }
val latinSubMenuItem = JMenuItem("ISO-LATIN-1")
latinSubMenuItem.addActionListener { logViewerView.handleChangeCharsetMenu(StandardCharsets.ISO_8859_1) }
val utf8SubMenuItem = JMenuItem("UTF-8")
utf8SubMenuItem.addActionListener { logViewerView.handleChangeCharsetMenu(StandardCharsets.UTF_8) }
val utf16beSubMenuItem = JMenuItem("UTF-16 Big Endian")
utf16beSubMenuItem.addActionListener { logViewerView.handleChangeCharsetMenu(StandardCharsets.UTF_16BE) }
val utf16leSubMenuItem = JMenuItem("UTF-16 Little Endian")
utf16leSubMenuItem.addActionListener { logViewerView.handleChangeCharsetMenu(StandardCharsets.UTF_16LE) }
val utf16SubMenuItem = JMenuItem("UTF-16")
utf16SubMenuItem.addActionListener { logViewerView.handleChangeCharsetMenu(StandardCharsets.UTF_16) }
menu.add(asciiSubMenuItem)
menu.add(latinSubMenuItem)
menu.add(utf8SubMenuItem)
menu.add(utf16beSubMenuItem)
menu.add(utf16leSubMenuItem)
menu.add(utf16SubMenuItem)
}


private fun openUserGuide() {
try {
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/com/tibagni/logviewer/log/FileLogReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
Expand All @@ -23,7 +23,7 @@ public FileLogReader(File[] logFiles) {
}

@Override
public void readLogs() throws LogReaderException {
public void readLogs(Charset charset) throws LogReaderException {
if (isClosed) {
throw new IllegalStateException("Reader already closed");
}
Expand All @@ -36,19 +36,19 @@ public void readLogs() throws LogReaderException {
try {
for (File logFile : logFiles) {
currentFile = logFile;
logStrings.put(currentFile.getPath(), readFile(currentFile));
logStrings.put(currentFile.getPath(), readFile(currentFile, charset));
}

} catch (IOException e) {
throw new LogReaderException("Error reading: " + currentFile, e);
}
}

private String readFile(File file) throws IOException {
private String readFile(File file, Charset charset) throws IOException {
String line;
StringBuilder builder = new StringBuilder();

try (BufferedReader reader = new BufferedReader(new FileReader(file, StandardCharsets.UTF_8))) {
try (BufferedReader reader = new BufferedReader(new FileReader(file, charset))) {
while ((line = reader.readLine()) != null) {
builder.append(line);
builder.append(StringUtils.LINE_SEPARATOR);
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/tibagni/logviewer/log/LogReader.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package com.tibagni.logviewer.log;

import java.nio.charset.Charset;
import java.util.Set;

public interface LogReader {
void readLogs() throws LogReaderException;
void readLogs(Charset charset) throws LogReaderException;

int size();

Expand Down
10 changes: 8 additions & 2 deletions src/main/java/com/tibagni/logviewer/log/parser/LogParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import com.tibagni.logviewer.util.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
Expand Down Expand Up @@ -39,10 +41,14 @@ public LogParser(LogReader logReader, ProgressReporter progressReporter) {
this.potentialBugReports = new HashMap<>();
}

public LogEntry[] parseLogs() throws LogReaderException {
// public LogEntry[] parseLogs() throws LogReaderException {
// return parseLogs(StandardCharsets.UTF_8);
// }

public LogEntry[] parseLogs(Charset charset) throws LogReaderException {
ensureState();

logReader.readLogs();
logReader.readLogs(charset);
Set<String> availableLogs = logReader.getAvailableLogPaths();

int logsRead = 0;
Expand Down
21 changes: 11 additions & 10 deletions src/test/java/com/tibagni/logviewer/LogViewerPresenterTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import org.mockito.Mockito.*
import org.mockito.MockitoAnnotations
import java.awt.Color
import java.io.File
import java.nio.charset.StandardCharsets

class LogViewerPresenterTests {
@Mock
Expand Down Expand Up @@ -1855,7 +1856,7 @@ class LogViewerPresenterTests {

presenter.loadLogs(inputLogFiles)

verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull())
verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull(), anyOrNull())
verify(view).showFilteredLogs(any())
verify(view).showLogs(any())
verify(view, never()).showMyLogs(any())
Expand Down Expand Up @@ -1885,7 +1886,7 @@ class LogViewerPresenterTests {

presenter.loadLogs(inputLogFiles)

verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull())
verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull(), anyOrNull())
verify(view).showFilteredLogs(any())
verify(view).showLogs(any())
verify(view).showMyLogs(any())
Expand All @@ -1909,7 +1910,7 @@ class LogViewerPresenterTests {

presenter.loadLogs(inputLogFiles)

verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull())
verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull(), anyOrNull())
verify(view).showFilteredLogs(any())
verify(view).showLogs(any())
verify(view).showAvailableLogStreams(any())
Expand All @@ -1934,7 +1935,7 @@ class LogViewerPresenterTests {

presenter.loadLogs(inputLogFiles)

verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull())
verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull(), anyOrNull())

// It should be called twice: first when the logs are loaded and then when filters are applied
verify(view, times(2)).showFilteredLogs(any())
Expand Down Expand Up @@ -1962,7 +1963,7 @@ class LogViewerPresenterTests {

presenter.loadLogs(inputLogFiles)

verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull())
verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull(), anyOrNull())

// It should be called twice: first when the logs are loaded and then when filters are applied
verify(view, times(2)).showFilteredLogs(any())
Expand Down Expand Up @@ -1991,7 +1992,7 @@ class LogViewerPresenterTests {

@Test
fun testLoadLogsInvalidFile() {
`when`(mockLogsRepository.openLogFiles(anyOrNull(), anyOrNull())).thenThrow(
`when`(mockLogsRepository.openLogFiles(anyOrNull(), anyOrNull(), anyOrNull())).thenThrow(
OpenLogsException(
"test invalid file message",
Exception()
Expand Down Expand Up @@ -2024,7 +2025,7 @@ class LogViewerPresenterTests {

presenter.loadLogs(inputLogFiles)

verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull())
verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull(), anyOrNull())
verify(view).showFilteredLogs(any())
verify(view).showLogs(any())
verify(view).showAvailableLogStreams(any())
Expand All @@ -2048,7 +2049,7 @@ class LogViewerPresenterTests {

presenter.loadLogs(inputLogFiles)

verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull())
verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull(), anyOrNull())
verify(view).showFilteredLogs(any())
verify(view).showLogs(any())
verify(view).showAvailableLogStreams(any())
Expand All @@ -2070,7 +2071,7 @@ class LogViewerPresenterTests {

presenter.loadLogs(inputLogFiles)

verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull())
verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull(), anyOrNull())
verify(view).showFilteredLogs(any())
verify(view).showLogs(any())
verify(view).showAvailableLogStreams(any())
Expand All @@ -2094,7 +2095,7 @@ class LogViewerPresenterTests {

presenter.loadLogs(inputLogFiles)

verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull())
verify(mockLogsRepository).openLogFiles(eqOrNull(inputLogFiles), anyOrNull(), anyOrNull())
verify(view).showFilteredLogs(any())
verify(view).showLogs(any())
verify(view).showAvailableLogStreams(any())
Expand Down
11 changes: 6 additions & 5 deletions src/test/java/com/tibagni/logviewer/LogsRepositoryTests.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.MockitoAnnotations
import java.io.File
import java.nio.charset.StandardCharsets

class LogsRepositoryTests {
private var temporaryFiles: Array<File>? = null
Expand Down Expand Up @@ -57,7 +58,7 @@ class LogsRepositoryTests {
@Test
fun testOpenSingleLogFile() {
val temporaryLogFile = createTempLogFiles("log")
logsRepository.openLogFiles(temporaryLogFile, mockProgressReporter)
logsRepository.openLogFiles(temporaryLogFile, StandardCharsets.UTF_8, mockProgressReporter)

verify(mockProgressReporter, atLeastOnce()).onProgress(anyInt(), anyString())
verify(mockProgressReporter, never()).failProgress()
Expand All @@ -70,7 +71,7 @@ class LogsRepositoryTests {
fun testOpenMultipleLogFilesOneStream() {
val temporaryLogFiles = createTempLogFiles("log", "log2")

logsRepository.openLogFiles(temporaryLogFiles, mockProgressReporter)
logsRepository.openLogFiles(temporaryLogFiles, StandardCharsets.UTF_8, mockProgressReporter)

verify(mockProgressReporter, atLeastOnce()).onProgress(anyInt(), anyString())
verify(mockProgressReporter, never()).failProgress()
Expand All @@ -83,7 +84,7 @@ class LogsRepositoryTests {
fun testOpenMultipleLogFilesMultipleStreams() {
val temporaryLogFiles = createTempLogFiles("main", "system")

logsRepository.openLogFiles(temporaryLogFiles, mockProgressReporter)
logsRepository.openLogFiles(temporaryLogFiles, StandardCharsets.UTF_8, mockProgressReporter)

verify(mockProgressReporter, atLeastOnce()).onProgress(anyInt(), anyString())
verify(mockProgressReporter, never()).failProgress()
Expand All @@ -96,7 +97,7 @@ class LogsRepositoryTests {
fun testOpenEmptyLogFiles() {
val temporaryLogFiles = createTempLogEmptyFiles("main")

logsRepository.openLogFiles(temporaryLogFiles, mockProgressReporter)
logsRepository.openLogFiles(temporaryLogFiles, StandardCharsets.UTF_8, mockProgressReporter)

verify(mockProgressReporter, atLeastOnce()).onProgress(anyInt(), anyString())
verify(mockProgressReporter, never()).failProgress()
Expand All @@ -108,7 +109,7 @@ class LogsRepositoryTests {
fun testOpenLogFilesInvalidFiles() {
val temporaryInvalidLogFile = File("invalid")

logsRepository.openLogFiles(arrayOf(temporaryInvalidLogFile), mockProgressReporter)
logsRepository.openLogFiles(arrayOf(temporaryInvalidLogFile), StandardCharsets.UTF_8, mockProgressReporter)
}

@Test
Expand Down
Loading

0 comments on commit c62448f

Please sign in to comment.