aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/eu/olli/cowlection/search
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/eu/olli/cowlection/search')
-rw-r--r--src/main/java/eu/olli/cowlection/search/GuiDateField.java37
-rw-r--r--src/main/java/eu/olli/cowlection/search/GuiSearch.java603
-rw-r--r--src/main/java/eu/olli/cowlection/search/GuiTooltip.java50
-rw-r--r--src/main/java/eu/olli/cowlection/search/LogFilesSearcher.java181
4 files changed, 0 insertions, 871 deletions
diff --git a/src/main/java/eu/olli/cowlection/search/GuiDateField.java b/src/main/java/eu/olli/cowlection/search/GuiDateField.java
deleted file mode 100644
index bb08a02..0000000
--- a/src/main/java/eu/olli/cowlection/search/GuiDateField.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package eu.olli.cowlection.search;
-
-import net.minecraft.client.gui.FontRenderer;
-import net.minecraft.client.gui.GuiTextField;
-
-import java.time.LocalDate;
-import java.time.format.DateTimeParseException;
-
-class GuiDateField extends GuiTextField {
- GuiDateField(int componentId, FontRenderer fontrendererObj, int x, int y, int width, int height) {
- super(componentId, fontrendererObj, x, y, width, height);
- }
-
- LocalDate getDate() {
- try {
- return LocalDate.parse(this.getText());
- } catch (DateTimeParseException e) {
- return LocalDate.now();
- }
- }
-
- boolean validateDate() {
- try {
- LocalDate localDate = LocalDate.parse(this.getText());
- if (localDate.isAfter(LocalDate.now()) || localDate.isBefore(LocalDate.ofYearDay(2009, 1))) {
- // searching for things written in the future isn't possible (yet). It is also not possible to perform a search before the existence of mc.
- setTextColor(0xFFFF3333);
- return false;
- }
- } catch (DateTimeParseException e) {
- setTextColor(0xFFFF3333);
- return false;
- }
- setTextColor(0xFFFFFF);
- return true;
- }
-}
diff --git a/src/main/java/eu/olli/cowlection/search/GuiSearch.java b/src/main/java/eu/olli/cowlection/search/GuiSearch.java
deleted file mode 100644
index d693e59..0000000
--- a/src/main/java/eu/olli/cowlection/search/GuiSearch.java
+++ /dev/null
@@ -1,603 +0,0 @@
-package eu.olli.cowlection.search;
-
-import com.google.common.util.concurrent.ThreadFactoryBuilder;
-import com.mojang.realmsclient.util.Pair;
-import eu.olli.cowlection.Cowlection;
-import eu.olli.cowlection.config.MooConfig;
-import eu.olli.cowlection.data.LogEntry;
-import eu.olli.cowlection.util.Utils;
-import net.minecraft.client.Minecraft;
-import net.minecraft.client.gui.*;
-import net.minecraft.client.renderer.GlStateManager;
-import net.minecraft.client.renderer.Tessellator;
-import net.minecraft.util.ChatComponentText;
-import net.minecraft.util.EnumChatFormatting;
-import net.minecraft.util.IChatComponent;
-import net.minecraftforge.common.ForgeVersion;
-import net.minecraftforge.fml.client.GuiScrollingList;
-import net.minecraftforge.fml.client.config.GuiButtonExt;
-import net.minecraftforge.fml.client.config.GuiCheckBox;
-import net.minecraftforge.fml.client.config.GuiUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.exception.ExceptionUtils;
-import org.apache.commons.lang3.reflect.FieldUtils;
-import org.apache.commons.lang3.tuple.ImmutableTriple;
-import org.lwjgl.input.Keyboard;
-import org.lwjgl.opengl.GL11;
-
-import java.awt.*;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.lang.reflect.Field;
-import java.nio.file.Files;
-import java.nio.file.StandardCopyOption;
-import java.time.LocalDate;
-import java.time.format.DateTimeFormatter;
-import java.util.List;
-import java.util.*;
-import java.util.concurrent.*;
-import java.util.stream.Collectors;
-import java.util.zip.GZIPInputStream;
-
-public class GuiSearch extends GuiScreen {
- private static final String SEARCH_QUERY_PLACE_HOLDER = "Search for...";
- private final File mcLogOutputFile;
- /**
- * @see Executors#newCachedThreadPool()
- */
- private final ExecutorService executorService = new ThreadPoolExecutor(0, 1,
- 60L, TimeUnit.SECONDS,
- new LinkedBlockingQueue<>(), new ThreadFactoryBuilder().setNameFormat(Cowlection.MODID + "-logfilesearcher-%d").build());
- // data
- private String searchQuery;
- private boolean chatOnly;
- private boolean matchCase;
- private boolean removeFormatting;
- /**
- * Cached results are required after resizing the client
- */
- private List<LogEntry> searchResults;
- private LocalDate dateStart;
- private LocalDate dateEnd;
-
- // gui elements
- private GuiButton buttonSearch;
- private GuiButton buttonClose;
- private GuiButton buttonHelp;
- private GuiCheckBox checkboxChatOnly;
- private GuiCheckBox checkboxMatchCase;
- private GuiCheckBox checkboxRemoveFormatting;
- private GuiTextField fieldSearchQuery;
- private GuiDateField fieldDateStart;
- private GuiDateField fieldDateEnd;
- private SearchResults guiSearchResults;
- private List<GuiTooltip> guiTooltips;
- private boolean isSearchInProgress;
- private String analyzedFiles;
- private String analyzedFilesWithHits;
- private boolean areEntriesSearchResults;
-
- public GuiSearch(File configDirectory) {
- this.mcLogOutputFile = new File(configDirectory, "mc-log.txt");
- try {
- mcLogOutputFile.createNewFile();
- } catch (IOException e) {
- e.printStackTrace();
- }
- this.searchQuery = SEARCH_QUERY_PLACE_HOLDER;
- this.searchResults = new ArrayList<>();
- this.dateStart = MooConfig.calculateStartDate();
- this.dateEnd = LocalDate.now();
- this.chatOnly = true;
- }
-
- /**
- * Adds the buttons (and other controls) to the screen in question. Called when the GUI is displayed and when the
- * window resizes, the buttonList is cleared beforehand.
- */
- @Override
- public void initGui() {
- this.guiTooltips = new ArrayList<>();
-
- this.fieldSearchQuery = new GuiTextField(42, this.fontRendererObj, this.width / 2 - 100, 13, 200, 20);
- this.fieldSearchQuery.setMaxStringLength(255);
- this.fieldSearchQuery.setText(searchQuery);
- if (SEARCH_QUERY_PLACE_HOLDER.equals(searchQuery)) {
- this.fieldSearchQuery.setFocused(true);
- this.fieldSearchQuery.setSelectionPos(0);
- }
-
- // date field: start
- this.fieldDateStart = new GuiDateField(50, this.fontRendererObj, this.width / 2 + 110, 15, 70, 15);
- this.fieldDateStart.setText(dateStart.toString());
- addTooltip(fieldDateStart, Arrays.asList(EnumChatFormatting.YELLOW + "Start date", "" + EnumChatFormatting.GRAY + EnumChatFormatting.ITALIC + "Format: " + EnumChatFormatting.RESET + "year-month-day"));
- // date field: end
- this.fieldDateEnd = new GuiDateField(51, this.fontRendererObj, this.width / 2 + 110, 35, 70, 15);
- this.fieldDateEnd.setText(dateEnd.toString());
- addTooltip(fieldDateEnd, Arrays.asList(EnumChatFormatting.YELLOW + "End date", "" + EnumChatFormatting.GRAY + EnumChatFormatting.ITALIC + "Format: " + EnumChatFormatting.RESET + "year-month-day"));
-
- // close
- this.buttonList.add(this.buttonClose = new GuiButtonExt(0, this.width - 25, 3, 22, 20, EnumChatFormatting.RED + "X"));
- addTooltip(buttonClose, Arrays.asList(EnumChatFormatting.RED + "Close search interface", "" + EnumChatFormatting.GRAY + EnumChatFormatting.ITALIC + "Hint:" + EnumChatFormatting.RESET + " alternatively press ESC"));
- // help
- this.buttonList.add(this.buttonHelp = new GuiButtonExt(1, this.width - 25 - 25, 3, 22, 20, "?"));
- addTooltip(buttonHelp, Collections.singletonList(EnumChatFormatting.YELLOW + "Show help"));
-
- // chatOnly
- this.buttonList.add(this.checkboxChatOnly = new GuiCheckBox(21, this.width / 2 - 100, 35, " Chatbox only", chatOnly));
- addTooltip(checkboxChatOnly, Collections.singletonList(EnumChatFormatting.YELLOW + "Should " + EnumChatFormatting.GOLD + "only " + EnumChatFormatting.YELLOW + "results that have " + EnumChatFormatting.GOLD + "appeared in the chat box " + EnumChatFormatting.YELLOW + "be displayed?\n"
- + EnumChatFormatting.GRAY + "For example, this " + EnumChatFormatting.WHITE + "excludes error messages" + EnumChatFormatting.GRAY + " but still " + EnumChatFormatting.WHITE + "includes messages sent by a server" + EnumChatFormatting.GRAY + "."));
- // matchCase
- this.buttonList.add(this.checkboxMatchCase = new GuiCheckBox(20, this.width / 2 - 100, 45, " Match case", matchCase));
- addTooltip(checkboxMatchCase, Collections.singletonList(EnumChatFormatting.YELLOW + "Should the search be " + EnumChatFormatting.GOLD + "case-sensitive" + EnumChatFormatting.YELLOW + "?"));
- // removeFormatting
- this.buttonList.add(this.checkboxRemoveFormatting = new GuiCheckBox(22, this.width / 2 - 100, 55, " Remove formatting", removeFormatting));
- addTooltip(checkboxRemoveFormatting, Collections.singletonList(EnumChatFormatting.YELLOW + "Should " + EnumChatFormatting.GOLD + "formatting " + EnumChatFormatting.YELLOW + "and " + EnumChatFormatting.GOLD + "color codes " + EnumChatFormatting.YELLOW + "be " + EnumChatFormatting.GOLD + "removed " + EnumChatFormatting.YELLOW + "from the search results?"));
- // search
- this.buttonList.add(this.buttonSearch = new GuiButtonExt(100, this.width / 2 + 40, 40, 60, 20, "Search"));
-
- this.guiSearchResults = new SearchResults(70);
- this.guiSearchResults.setResults(searchResults);
-
- this.setIsSearchInProgress(isSearchInProgress);
-
- boolean isStartDateValid = fieldDateStart.validateDate();
- boolean isEndDateValid = fieldDateEnd.validateDate();
- this.buttonSearch.enabled = !isSearchInProgress && this.fieldSearchQuery.getText().trim().length() > 1 && !this.fieldSearchQuery.getText().startsWith(SEARCH_QUERY_PLACE_HOLDER) && isStartDateValid && isEndDateValid && !dateStart.isAfter(dateEnd);
-
- if (isStartDateValid && isEndDateValid && dateStart.isAfter(dateEnd)) {
- fieldDateStart.setTextColor(0xFFDD3333);
- fieldDateEnd.setTextColor(0xFFCC3333);
- }
- }
-
- private <T extends Gui> void addTooltip(T field, List<String> tooltip) {
- GuiTooltip guiTooltip = new GuiTooltip(field, tooltip);
- this.guiTooltips.add(guiTooltip);
- }
-
- @Override
- public void updateScreen() {
- fieldSearchQuery.updateCursorCounter();
- fieldDateStart.updateCursorCounter();
- fieldDateEnd.updateCursorCounter();
- }
-
- @Override
- protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException {
- // allow clicks on 'close' button even while a search is in progress
- super.mouseClicked(mouseX, mouseY, mouseButton);
- if (isSearchInProgress) {
- // search in progress, abort
- return;
- }
- fieldSearchQuery.mouseClicked(mouseX, mouseY, mouseButton);
- fieldDateStart.mouseClicked(mouseX, mouseY, mouseButton);
- fieldDateEnd.mouseClicked(mouseX, mouseY, mouseButton);
- }
-
- @Override
- protected void keyTyped(char typedChar, int keyCode) throws IOException {
- if (isSearchInProgress && keyCode != Keyboard.KEY_ESCAPE) {
- // search in progress, don't process key typed - but allow escape to exit gui
- return;
- }
- if (dateStart.isBefore(dateEnd)) {
- fieldDateStart.setTextColor(0xFFFFFFFF);
- fieldDateEnd.setTextColor(0xFFFFFFFF);
- }
- if (keyCode == Keyboard.KEY_RETURN && this.fieldSearchQuery.isFocused()) {
- // perform search
- actionPerformed(buttonSearch);
- } else if (this.fieldSearchQuery.textboxKeyTyped(typedChar, keyCode)) {
- searchQuery = this.fieldSearchQuery.getText();
- } else if (this.fieldDateStart.textboxKeyTyped(typedChar, keyCode)) {
- if (fieldDateStart.validateDate()) {
- dateStart = fieldDateStart.getDate();
- }
- } else if (this.fieldDateEnd.textboxKeyTyped(typedChar, keyCode)) {
- if (fieldDateEnd.validateDate()) {
- dateEnd = fieldDateEnd.getDate();
- }
- } else if (GuiScreen.isKeyComboCtrlA(keyCode)) {
- // copy all search results
- String searchResults = guiSearchResults.getAllSearchResults();
- if (!searchResults.isEmpty()) {
- GuiScreen.setClipboardString(searchResults);
- }
- } else if (GuiScreen.isKeyComboCtrlC(keyCode)) {
- // copy current selected entry
- LogEntry selectedSearchResult = guiSearchResults.getSelectedSearchResult();
- if (selectedSearchResult != null) {
- GuiScreen.setClipboardString(EnumChatFormatting.getTextWithoutFormattingCodes(selectedSearchResult.getMessage()));
- }
- } else if (keyCode == Keyboard.KEY_C && isCtrlKeyDown() && isShiftKeyDown() && !isAltKeyDown()) {
- // copy current selected entry with formatting codes
- LogEntry selectedSearchResult = guiSearchResults.getSelectedSearchResult();
- if (selectedSearchResult != null) {
- GuiScreen.setClipboardString(selectedSearchResult.getMessage());
- }
- } else {
- if (keyCode == Keyboard.KEY_ESCAPE) {
- guiSearchResults = null;
- }
- super.keyTyped(typedChar, keyCode);
- }
-
- boolean isStartDateValid = fieldDateStart.validateDate();
- boolean isEndDateValid = fieldDateEnd.validateDate();
- this.buttonSearch.enabled = !isSearchInProgress && searchQuery.trim().length() > 1 && !searchQuery.startsWith(SEARCH_QUERY_PLACE_HOLDER) && isStartDateValid && isEndDateValid && !dateStart.isAfter(dateEnd);
-
- if (isStartDateValid && isEndDateValid && dateStart.isAfter(dateEnd)) {
- fieldDateStart.setTextColor(0xFFDD3333);
- fieldDateEnd.setTextColor(0xFFCC3333);
- }
- }
-
- @Override
- public void drawScreen(int mouseX, int mouseY, float partialTicks) {
- this.drawDefaultBackground();
- this.drawCenteredString(this.fontRendererObj, EnumChatFormatting.BOLD + "Minecraft Log Search", this.width / 2, 2, 0xFFFFFF);
- this.fieldSearchQuery.drawTextBox();
- this.fieldDateStart.drawTextBox();
- this.fieldDateEnd.drawTextBox();
- this.guiSearchResults.drawScreen(mouseX, mouseY, partialTicks);
-
- super.drawScreen(mouseX, mouseY, partialTicks);
-
- for (GuiTooltip guiTooltip : guiTooltips) {
- if (guiTooltip.checkHover(mouseX, mouseY)) {
- drawHoveringText(guiTooltip.getText(), mouseX, mouseY, 300);
- // only one tooltip can be displayed at a time: break!
- break;
- }
- }
- }
-
- @Override
- protected void actionPerformed(GuiButton button) throws IOException {
- if (button == this.buttonClose && button.enabled) {
- guiSearchResults = null;
- this.mc.setIngameFocus();
- }
- if (isSearchInProgress || !button.enabled) {
- return;
- }
- if (button == this.buttonSearch) {
- setIsSearchInProgress(true);
-
- executorService.execute(() -> {
- try {
- ImmutableTriple<Integer, Integer, List<LogEntry>> searchResultsData = new LogFilesSearcher().searchFor(this.fieldSearchQuery.getText(), checkboxChatOnly.isChecked(), checkboxMatchCase.isChecked(), checkboxRemoveFormatting.isChecked(), dateStart, dateEnd);
- this.searchResults = searchResultsData.right;
- this.analyzedFiles = "Analyzed files: " + EnumChatFormatting.WHITE + searchResultsData.left;
- this.analyzedFilesWithHits = "Files with hits: " + EnumChatFormatting.WHITE + searchResultsData.middle;
- if (this.searchResults.isEmpty()) {
- this.searchResults.add(new LogEntry(EnumChatFormatting.ITALIC + "No results"));
- areEntriesSearchResults = false;
- } else {
- areEntriesSearchResults = true;
- }
- } catch (IOException e) {
- System.err.println("Error reading/parsing file log files:");
- e.printStackTrace();
- if (e.getStackTrace().length > 0) {
- searchResults.add(new LogEntry(StringUtils.replaceEach(ExceptionUtils.getStackTrace(e), new String[]{"\t", "\r\n"}, new String[]{" ", "\n"})));
- }
- }
- Minecraft.getMinecraft().addScheduledTask(() -> {
- this.guiSearchResults.setResults(this.searchResults);
- setIsSearchInProgress(false);
- });
- });
- } else if (button == checkboxChatOnly) {
- chatOnly = checkboxChatOnly.isChecked();
- } else if (button == checkboxMatchCase) {
- matchCase = checkboxMatchCase.isChecked();
- } else if (button == checkboxRemoveFormatting) {
- removeFormatting = checkboxRemoveFormatting.isChecked();
- } else if (button == buttonHelp) {
- this.areEntriesSearchResults = false;
- this.searchResults.clear();
- this.searchResults.add(new LogEntry("" + EnumChatFormatting.GOLD + EnumChatFormatting.BOLD + "Initial setup/Configuration " + EnumChatFormatting.GRAY + EnumChatFormatting.ITALIC + "/moo config"));
- this.searchResults.add(new LogEntry(EnumChatFormatting.GOLD + " 1) " + EnumChatFormatting.RESET + "Configure directories that should be scanned for log files (\"Directories with Minecraft log files\")"));
- this.searchResults.add(new LogEntry(EnumChatFormatting.GOLD + " 2) " + EnumChatFormatting.RESET + "Set default starting date (\"Start date for log file search\")"));
- this.searchResults.add(new LogEntry("" + EnumChatFormatting.GOLD + EnumChatFormatting.BOLD + "Performing a search " + EnumChatFormatting.GRAY + EnumChatFormatting.ITALIC + "/moo search"));
- this.searchResults.add(new LogEntry(EnumChatFormatting.GOLD + " 1) " + EnumChatFormatting.RESET + "Enter search term"));
- this.searchResults.add(new LogEntry(EnumChatFormatting.GOLD + " 2) " + EnumChatFormatting.RESET + "Adjust start and end date"));
- this.searchResults.add(new LogEntry(EnumChatFormatting.GOLD + " 3) " + EnumChatFormatting.RESET + "Select desired options (match case, ...)"));
- this.searchResults.add(new LogEntry(EnumChatFormatting.GOLD + " 4) " + EnumChatFormatting.RESET + "Click 'Search'"));
- this.searchResults.add(new LogEntry("" + EnumChatFormatting.GOLD + EnumChatFormatting.BOLD + "Search results"));
- this.searchResults.add(new LogEntry(EnumChatFormatting.GOLD + " - " + EnumChatFormatting.YELLOW + "CTRL + C " + EnumChatFormatting.RESET + "to copy selected search result"));
- this.searchResults.add(new LogEntry(EnumChatFormatting.GOLD + " - " + EnumChatFormatting.YELLOW + "CTRL + Shift + C " + EnumChatFormatting.RESET + "to copy selected search result " + EnumChatFormatting.ITALIC + "with" + EnumChatFormatting.RESET + " formatting codes"));
- this.searchResults.add(new LogEntry(EnumChatFormatting.GOLD + " - " + EnumChatFormatting.YELLOW + "CTRL + A " + EnumChatFormatting.RESET + "to copy all search results"));
- this.searchResults.add(new LogEntry(EnumChatFormatting.GOLD + " - " + EnumChatFormatting.YELLOW + "Double click search result " + EnumChatFormatting.RESET + "to open corresponding log file in default text editor"));
- this.guiSearchResults.setResults(searchResults);
- }
- }
-
- private void setIsSearchInProgress(boolean isSearchInProgress) {
- this.isSearchInProgress = isSearchInProgress;
- buttonSearch.enabled = !isSearchInProgress;
- fieldSearchQuery.setEnabled(!isSearchInProgress);
- fieldDateStart.setEnabled(!isSearchInProgress);
- fieldDateEnd.setEnabled(!isSearchInProgress);
- checkboxChatOnly.enabled = !isSearchInProgress;
- checkboxMatchCase.enabled = !isSearchInProgress;
- checkboxRemoveFormatting.enabled = !isSearchInProgress;
- if (isSearchInProgress) {
- fieldSearchQuery.setFocused(false);
- fieldDateStart.setFocused(false);
- fieldDateEnd.setFocused(false);
- buttonSearch.displayString = EnumChatFormatting.ITALIC + "Searching";
- searchResults.clear();
- guiSearchResults.clearResults();
- analyzedFiles = null;
- analyzedFilesWithHits = null;
- } else {
- buttonSearch.displayString = "Search";
- }
- }
-
- private void drawHoveringText(List<String> textLines, int mouseX, int mouseY, int maxTextWidth) {
- if (ForgeVersion.getBuildVersion() < 1808) {
- // we're running a forge version from before 24 March 2016 (http://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.8.9.html for reference)
- // using mc built-in method
- drawHoveringText(textLines, mouseX, mouseY, fontRendererObj);
- } else {
- // we're on a newer forge version, so we can use the improved tooltip rendering added in 1.8.9-11.15.1.1808 (released 03/24/16 09:25 PM) in this pull request: https://github.com/MinecraftForge/MinecraftForge/pull/2649
- GuiUtils.drawHoveringText(textLines, mouseX, mouseY, width, height, maxTextWidth, fontRendererObj);
- }
- }
-
- /**
- * List gui element similar to GuiModList.Info
- */
- class SearchResults extends GuiScrollingList {
- private final String[] spinner = new String[]{"oooooo", "Oooooo", "oOoooo", "ooOooo", "oooOoo", "ooooOo", "oooooO"};
- private final DateTimeFormatter coloredDateFormatter = DateTimeFormatter.ofPattern(EnumChatFormatting.GRAY + "HH" + EnumChatFormatting.DARK_GRAY + ":" + EnumChatFormatting.GRAY + "mm" + EnumChatFormatting.DARK_GRAY + ":" + EnumChatFormatting.GRAY + "ss");
- private List<LogEntry> rawResults;
- private List<IChatComponent> slotsData;
- /**
- * key: slot id of 1st line of a search result (if multi-line-result), value: search result id
- */
- private NavigableMap<Integer, Integer> searchResultEntries;
- private Pair<Long, String> errorMessage;
- private String resultsCount;
-
- SearchResults(int marginTop) {
- super(GuiSearch.this.mc,
- GuiSearch.this.width - 10, // 5 pixel margin each
- GuiSearch.this.height - marginTop - 5,
- marginTop, GuiSearch.this.height - 5,
- 5, 12,
- GuiSearch.this.width,
- GuiSearch.this.height);
- this.rawResults = Collections.emptyList();
- this.slotsData = Collections.emptyList();
- this.searchResultEntries = Collections.emptyNavigableMap();
- }
-
- @Override
- public void drawScreen(int mouseX, int mouseY, float partialTicks) {
- super.drawScreen(mouseX, mouseY, partialTicks);
- if (isSearchInProgress) {
- // spinner taken from IProgressMeter and GuiAchievements#drawScreen
- GuiSearch.this.drawCenteredString(GuiSearch.this.fontRendererObj, "Searching for '" + GuiSearch.this.searchQuery + "'", GuiSearch.this.width / 2, GuiSearch.this.height / 2, 16777215);
- GuiSearch.this.drawCenteredString(GuiSearch.this.fontRendererObj, spinner[(int) (Minecraft.getSystemTime() / 150L % (long) spinner.length)], GuiSearch.this.width / 2, GuiSearch.this.height / 2 + GuiSearch.this.fontRendererObj.FONT_HEIGHT * 2, 16777215);
- }
- int hoveredSlotId = this.func_27256_c(mouseX, mouseY);
- if (hoveredSlotId >= 0 && mouseY > top && mouseY < bottom) {
- float scrollDistance = getScrollDistance();
- if (scrollDistance != Float.MIN_VALUE) {
- // draw hovered entry details
-
- int hoveredSearchResultId = getSearchResultIdBySlotId(hoveredSlotId);
- LogEntry hoveredEntry = getSearchResultByResultId(hoveredSearchResultId);
- if (hoveredEntry != null && !hoveredEntry.isError()) {
- // draw 'tooltips' in the top left corner
- drawString(fontRendererObj, "Log file: ", 2, 2, 0xff888888);
- GlStateManager.pushMatrix();
- float scaleFactor = 0.75f;
- GL11.glScalef(scaleFactor, scaleFactor, scaleFactor);
- fontRendererObj.drawSplitString(EnumChatFormatting.GRAY + Utils.toRealPath(hoveredEntry.getFilePath()), 5, (int) ((4 + fontRendererObj.FONT_HEIGHT) * (1 / scaleFactor)), (int) ((GuiSearch.this.fieldSearchQuery.xPosition - 8) * (1 / scaleFactor)), 0xff888888);
- GlStateManager.popMatrix();
- drawString(fontRendererObj, "Result: " + EnumChatFormatting.WHITE + (hoveredSearchResultId + 1) + EnumChatFormatting.RESET + "/" + EnumChatFormatting.WHITE + this.rawResults.size(), 8, 48, 0xff888888);
- drawString(fontRendererObj, "Time: " + hoveredEntry.getTime().format(coloredDateFormatter), 8, 58, 0xff888888);
- }
-
- // formula from GuiScrollingList#drawScreen slotTop
- int baseY = this.top + /* border: */4 - (int) scrollDistance;
-
- // highlight multiline search results
- Integer resultIndexStart = searchResultEntries.floorKey(hoveredSlotId);
- Integer resultIndexEnd = searchResultEntries.higherKey(hoveredSlotId);
-
- if (resultIndexStart == null) {
- return;
- } else if (resultIndexEnd == null) {
- // last result entry
- resultIndexEnd = getSize();
- }
-
- int slotTop = baseY + resultIndexStart * this.slotHeight - 2;
- int slotBottom = baseY + resultIndexEnd * this.slotHeight - 2;
- drawRect(this.left, Math.max(slotTop, top), right - /* scrollBar: */7, Math.min(slotBottom, bottom), 0x22ffffff);
- }
- } else if (areEntriesSearchResults) {
- if (analyzedFiles != null) {
- drawString(fontRendererObj, analyzedFiles, 8, 22, 0xff888888);
- }
- if (analyzedFilesWithHits != null) {
- drawString(fontRendererObj, analyzedFilesWithHits, 8, 32, 0xff888888);
- }
- if (resultsCount != null) {
- drawString(fontRendererObj, resultsCount, 8, 48, 0xff888888);
- }
- }
- if (errorMessage != null) {
- if (errorMessage.first().compareTo(System.currentTimeMillis()) > 0) {
- String errorText = "Error: " + EnumChatFormatting.RED + errorMessage.second();
- int stringWidth = fontRendererObj.getStringWidth(errorText);
- int margin = 5;
- int left = width / 2 - stringWidth / 2 - margin;
- int top = height / 2 - margin;
- drawRect(left, top, left + stringWidth + 2 * margin, top + fontRendererObj.FONT_HEIGHT + 2 * margin, 0xff000000);
- drawCenteredString(fontRendererObj, errorText,/* 2, 30*/width / 2, height / 2, 0xffDD1111);
- } else {
- errorMessage = null;
- }
- }
- }
-
- private float getScrollDistance() {
- Field scrollDistanceField = FieldUtils.getField(GuiScrollingList.class, "scrollDistance", true);
- if (scrollDistanceField == null) {
- // scrollDistance field not found in class GuiScrollingList
- return Float.MIN_VALUE;
- }
- try {
- return (float) scrollDistanceField.get(this);
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- return Float.MIN_VALUE;
- }
- }
-
- @Override
- protected int getSize() {
- return slotsData.size();
- }
-
- @Override
- protected void elementClicked(int index, boolean doubleClick) {
- if (doubleClick) {
- int searchResultIdBySlotId = getSearchResultIdBySlotId(index);
- LogEntry searchResult = rawResults.get(searchResultIdBySlotId);
- if (searchResult.getFilePath() == null) {
- setErrorMessage("This log entry is not from a file");
- return;
- }
- byte[] buffer = new byte[1024];
- String logFileName = Utils.toRealPath(searchResult.getFilePath());
- if (logFileName.endsWith("latest.log")) {
- try {
- Files.copy(searchResult.getFilePath(), mcLogOutputFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
- } catch (IOException e) {
- e.printStackTrace();
- }
- } else { // .log.gz
- String newLine = System.getProperty("line.separator");
- String fileHeader = "# Original filename: " + logFileName + newLine + "# Use CTRL + F to search for specific words" + newLine + newLine;
- try (GZIPInputStream logFileGzipped = new GZIPInputStream(new FileInputStream(logFileName));
- FileOutputStream logFileUnGzipped = new FileOutputStream(mcLogOutputFile)) {
- logFileUnGzipped.write(fileHeader.getBytes());
- int len;
- while ((len = logFileGzipped.read(buffer)) > 0) {
- logFileUnGzipped.write(buffer, 0, len);
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- try {
- Desktop.getDesktop().open(mcLogOutputFile);
- } catch (IOException e) {
- setErrorMessage("File extension .txt has no associated default editor");
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- setErrorMessage(e.getMessage()); // The file: <path> doesn't exist.
- e.printStackTrace();
- } catch (UnsupportedOperationException e) {
- setErrorMessage("Can't open files on this OS");
- e.printStackTrace();
- }
- }
- }
-
- private void setErrorMessage(String errorMessage) {
- int showDuration = 10000; // ms
- this.errorMessage = Pair.of(System.currentTimeMillis() + showDuration, errorMessage);
- }
-
- @Override
- protected boolean isSelected(int index) {
- return false;
- }
-
- @Override
- protected void drawBackground() {
- }
-
- @Override
- protected void drawSlot(int slotIdx, int entryRight, int slotTop, int slotBuffer, Tessellator tess) {
- int drawnResultIndex = searchResultEntries.floorKey(slotIdx);
- if (Objects.equals(searchResultEntries.floorKey(selectedIndex), drawnResultIndex)) {
- // highlight all lines of selected entry
- drawRect(this.left, slotTop - 2, entryRight, slotTop + slotHeight - 2, 0x99000000);
- }
- IChatComponent slotData = slotsData.get(slotIdx);
- if (slotData != null) {
- GlStateManager.enableBlend();
- GuiSearch.this.fontRendererObj.drawStringWithShadow(slotData.getFormattedText(), this.left + 4, slotTop, 0xFFFFFF);
- GlStateManager.disableAlpha();
- GlStateManager.disableBlend();
- }
- }
-
- private void setResults(List<LogEntry> searchResult) {
- this.rawResults = searchResult;
- this.slotsData = resizeContent(searchResult);
- if (GuiSearch.this.areEntriesSearchResults) {
- this.resultsCount = "Results: " + EnumChatFormatting.WHITE + this.rawResults.size();
- }
- }
-
- private void clearResults() {
- this.rawResults = Collections.emptyList();
- this.resultsCount = null;
- this.slotsData = resizeContent(Collections.emptyList());
- }
-
- private List<IChatComponent> resizeContent(List<LogEntry> searchResults) {
- this.searchResultEntries = new TreeMap<>();
- List<IChatComponent> slotsData = new ArrayList<>();
- for (int searchResultIndex = 0; searchResultIndex < searchResults.size(); searchResultIndex++) {
- LogEntry searchResult = searchResults.get(searchResultIndex);
-
- String searchResultEntry;
- if (searchResult.isError()) {
- searchResultEntry = searchResult.getMessage();
- } else {
- searchResultEntry = EnumChatFormatting.DARK_GRAY + searchResult.getTime().format(DateTimeFormatter.ISO_LOCAL_DATE) + " " + EnumChatFormatting.RESET + searchResult.getMessage();
- }
- searchResultEntries.put(slotsData.size(), searchResultIndex);
- List<IChatComponent> multilineResult = GuiUtilRenderComponents.splitText(new ChatComponentText(searchResultEntry), this.listWidth - 8, GuiSearch.this.fontRendererObj, false, true);
- slotsData.addAll(multilineResult);
- }
- return slotsData;
- }
-
- LogEntry getSelectedSearchResult() {
- int searchResultId = getSearchResultIdBySlotId(selectedIndex);
- return getSearchResultByResultId(searchResultId);
- }
-
- private LogEntry getSearchResultByResultId(int searchResultId) {
- return (searchResultId >= 0 && searchResultId < rawResults.size()) ? rawResults.get(searchResultId) : null;
- }
-
- private int getSearchResultIdBySlotId(int slotId) {
- Map.Entry<Integer, Integer> searchResultIds = searchResultEntries.floorEntry(slotId);
- return searchResultIds != null ? searchResultIds.getValue() : -1;
- }
-
- String getAllSearchResults() {
- return rawResults.stream().map(logEntry -> EnumChatFormatting.getTextWithoutFormattingCodes(logEntry.getMessage()))
- .collect(Collectors.joining("\n"));
- }
- }
-}
diff --git a/src/main/java/eu/olli/cowlection/search/GuiTooltip.java b/src/main/java/eu/olli/cowlection/search/GuiTooltip.java
deleted file mode 100644
index f76bf2d..0000000
--- a/src/main/java/eu/olli/cowlection/search/GuiTooltip.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package eu.olli.cowlection.search;
-
-import net.minecraft.client.gui.Gui;
-import net.minecraft.client.gui.GuiButton;
-import net.minecraft.client.gui.GuiTextField;
-import net.minecraftforge.fml.client.config.GuiCheckBox;
-import net.minecraftforge.fml.client.config.HoverChecker;
-
-import java.util.List;
-
-public class GuiTooltip {
- private final HoverChecker hoverChecker;
- private final List<String> tooltip;
-
- public <T extends Gui> GuiTooltip(T field, List<String> tooltip) {
- if (field instanceof GuiCheckBox) {
- // checkbox
- GuiCheckBox guiCheckBox = (GuiCheckBox) field;
- int top = guiCheckBox.yPosition;
- int bottom = guiCheckBox.yPosition + guiCheckBox.height;
- int left = guiCheckBox.xPosition;
- int right = guiCheckBox.xPosition + guiCheckBox.width;
-
- this.hoverChecker = new HoverChecker(top, bottom, left, right, 300);
- } else if (field instanceof GuiTextField) {
- // text field
- GuiTextField guiTextField = (GuiTextField) field;
- int top = guiTextField.yPosition;
- int bottom = guiTextField.yPosition + guiTextField.height;
- int left = guiTextField.xPosition;
- int right = guiTextField.xPosition + guiTextField.width;
-
- this.hoverChecker = new HoverChecker(top, bottom, left, right, 300);
- } else if (field instanceof GuiButton) {
- // button
- this.hoverChecker = new HoverChecker((GuiButton) field, 300);
- } else {
- throw new IllegalArgumentException("Tried to add a tooltip to an illegal field type: " + field.getClass());
- }
- this.tooltip = tooltip;
- }
-
- public List<String> getText() {
- return tooltip;
- }
-
- public boolean checkHover(int mouseX, int mouseY) {
- return hoverChecker.checkHover(mouseX, mouseY);
- }
-}
diff --git a/src/main/java/eu/olli/cowlection/search/LogFilesSearcher.java b/src/main/java/eu/olli/cowlection/search/LogFilesSearcher.java
deleted file mode 100644
index 7aeb2aa..0000000
--- a/src/main/java/eu/olli/cowlection/search/LogFilesSearcher.java
+++ /dev/null
@@ -1,181 +0,0 @@
-package eu.olli.cowlection.search;
-
-import eu.olli.cowlection.config.MooConfig;
-import eu.olli.cowlection.data.LogEntry;
-import net.minecraft.util.EnumChatFormatting;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.tuple.ImmutableTriple;
-
-import java.io.*;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.time.Instant;
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-import java.util.zip.GZIPInputStream;
-
-class LogFilesSearcher {
- /**
- * Log4j.xml PatternLayout: [%d{HH:mm:ss}] [%t/%level]: %msg%n
- * Log line: [TIME] [THREAD/LEVEL]: [CHAT] msg
- * examples:
- * - [13:33:37] [Client thread/INFO]: [CHAT] Hello World
- * - [08:15:42] [Client thread/ERROR]: Item entity 9001 has no item?!
- */
- private static final Pattern LOG4J_PATTERN = Pattern.compile("^\\[(?<timeHours>[\\d]{2}):(?<timeMinutes>[\\d]{2}):(?<timeSeconds>[\\d]{2})] \\[(?<thread>[^/]+)/(?<logLevel>[A-Z]+)]:(?<isChat> \\[CHAT])? (?<message>.*)$");
- private int analyzedFilesWithHits = 0;
-
- ImmutableTriple<Integer, Integer, List<LogEntry>> searchFor(String searchQuery, boolean chatOnly, boolean matchCase, boolean removeFormatting, LocalDate dateStart, LocalDate dateEnd) throws IOException {
- List<Path> files = new ArrayList<>();
- for (String logsDirPath : MooConfig.logsDirs) {
- File logsDir = new File(logsDirPath);
- if (logsDir.exists() && logsDir.isDirectory()) {
- try {
- files.addAll(fileList(logsDir, dateStart, dateEnd));
- } catch (IOException e) {
- throw throwIoException(logsDirPath, e);
- }
- }
- }
-
- if (files.isEmpty()) {
- throw new FileNotFoundException(EnumChatFormatting.DARK_RED + "ERROR: Couldn't find any Minecraft log files. Please check if the log file directories are set correctly (/moo config).");
- } else {
- List<LogEntry> searchResults = analyzeFiles(files, searchQuery, chatOnly, matchCase, removeFormatting)
- .stream().sorted(Comparator.comparing(LogEntry::getTime)).collect(Collectors.toList());
- return new ImmutableTriple<>(files.size(), analyzedFilesWithHits, searchResults);
- }
- }
-
- private List<LogEntry> analyzeFiles(List<Path> paths, String searchTerm, boolean chatOnly, boolean matchCase, boolean removeFormatting) throws IOException {
- List<LogEntry> searchResults = new ArrayList<>();
- for (Path path : paths) {
- boolean foundSearchTermInFile = false;
- try (BufferedReader in = (path.endsWith("latest.log")
- ? new BufferedReader(new InputStreamReader(new FileInputStream(path.toFile()))) // latest.log
- : new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(path.toFile())))))) { // ....log.gz
- String fileName = path.getFileName().toString(); // 2020-04-20-3.log.gz
- String date = fileName.equals("latest.log")
- ? LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE)
- : fileName.substring(0, fileName.lastIndexOf('-'));
- String content;
- LogEntry logEntry = null;
- while ((content = in.readLine()) != null) {
- Matcher logLineMatcher = LOG4J_PATTERN.matcher(content);
- if (logLineMatcher.matches()) { // current line is a new log entry
- if (logEntry != null) {
- // we had a previous log entry; analyze it!
- LogEntry result = analyzeLogEntry(logEntry, searchTerm, matchCase, removeFormatting);
- if (result != null) {
- searchResults.add(result);
- foundSearchTermInFile = true;
- }
- logEntry = null;
- }
- // handle first line of new log entry
- if (chatOnly && logLineMatcher.group("isChat") == null) {
- // not a chat log entry, although we're only searching for chat messages, abort!
- continue;
- }
- LocalDateTime dateTime = getDate(date, logLineMatcher);
- logEntry = new LogEntry(dateTime, path, logLineMatcher.group("message"));
- } else if (logEntry != null) {
- // multiline log entry
- logEntry.addLogLine(content);
- }
- }
- if (logEntry != null) {
- // end of file! analyze last log entry in file
- LogEntry result = analyzeLogEntry(logEntry, searchTerm, matchCase, removeFormatting);
- if (result != null) {
- searchResults.add(result);
- foundSearchTermInFile = true;
- }
- }
- if (foundSearchTermInFile) {
- analyzedFilesWithHits++;
- }
- } catch (IOException e) {
- throw throwIoException(path.toString(), e);
- }
- }
- return searchResults;
- }
-
- private LocalDateTime getDate(String date, Matcher logLineMatcher) {
- int year = Integer.parseInt(date.substring(0, 4));
- int month = Integer.parseInt(date.substring(5, 7));
- int day = Integer.parseInt(date.substring(8, 10));
- int hour = Integer.parseInt(logLineMatcher.group(1));
- int minute = Integer.parseInt(logLineMatcher.group(2));
- int sec = Integer.parseInt(logLineMatcher.group(3));
-
- return LocalDateTime.of(year, month, day, hour, minute, sec);
- }
-
- private LogEntry analyzeLogEntry(LogEntry logEntry, String searchTerms, boolean matchCase, boolean removeFormatting) {
- if (logEntry.getMessage().length() > 5000) {
- // avoid ultra long log entries
- return null;
- }
- logEntry.fixWeirdCharacters();
-
- if (removeFormatting) {
- logEntry.removeFormatting();
- }
- String logMessage = logEntry.getMessage();
- if (!matchCase) {
- if (!StringUtils.containsIgnoreCase(logMessage, searchTerms)) {
- // no result, abort
- return null;
- }
- } else if (!logMessage.contains(searchTerms)) {
- // no result, abort
- return null;
- }
-
- return logEntry;
- }
-
- private List<Path> fileList(File directory, LocalDate startDate, LocalDate endDate) throws IOException {
- List<Path> fileNames = new ArrayList<>();
- try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(directory.toPath())) {
- for (Path path : directoryStream) {
- if (path.toString().endsWith(".log.gz")) {
- String[] fileDate = path.getFileName().toString().split("-");
- if (fileDate.length == 4) {
- LocalDate fileLocalDate = LocalDate.of(Integer.parseInt(fileDate[0]),
- Integer.parseInt(fileDate[1]), Integer.parseInt(fileDate[2]));
-
- if (fileLocalDate.compareTo(startDate) >= 0 && fileLocalDate.compareTo(endDate) <= 0) {
- fileNames.add(path);
- }
- } else {
- System.err.println("Error with " + path.toString());
- }
- } else if (path.getFileName().toString().equals("latest.log")) {
- LocalDate lastModified = Instant.ofEpochMilli(path.toFile().lastModified()).atZone(ZoneId.systemDefault()).toLocalDate();
- if (!lastModified.isBefore(startDate) && !lastModified.isAfter(endDate)) {
- fileNames.add(path);
- }
- }
- }
- }
- return fileNames;
- }
-
- private IOException throwIoException(String file, IOException e) throws IOException {
- IOException ioException = new IOException(EnumChatFormatting.DARK_RED + "ERROR: An error occurred trying to read/parse '" + EnumChatFormatting.RED + file + EnumChatFormatting.DARK_RED + "'");
- ioException.setStackTrace(e.getStackTrace());
- throw ioException;
- }
-}