From a30569285592c652d18277eb638816381910ae70 Mon Sep 17 00:00:00 2001 From: Gino Augustine Date: Thu, 30 Nov 2023 14:25:37 +0530 Subject: [PATCH] Refactoring to reduce sonar code smell fixes (#4485) --------- Signed-off-by: Gino Augustine Co-authored-by: Vladimir Kotal --- .../indexer/analysis/AnalyzerGuru.java | 323 +++++++++--------- .../org/opengrok/indexer/analysis/Ctags.java | 1 + .../indexer/analysis/CtagsReader.java | 16 +- .../indexer/analysis/Definitions.java | 61 ++-- .../indexer/analysis/JFlexXrefUtils.java | 39 ++- .../indexer/analysis/hcl/HCLLexer.java | 6 +- .../analysis/yaml/YamlAnalyzerFactory.java | 5 +- .../indexer/configuration/ConfigMerge.java | 26 +- .../configuration/ConfigurationHelp.java | 47 +-- .../indexer/configuration/Filter.java | 39 +-- .../opengrok/indexer/configuration/Group.java | 11 +- .../indexer/configuration/IndexTimestamp.java | 2 +- .../indexer/framework/PluginClassLoader.java | 83 +++-- .../indexer/framework/PluginFramework.java | 19 +- .../indexer/history/AccuRevHistoryParser.java | 4 +- .../indexer/history/BazaarHistoryParser.java | 187 ++++++---- .../indexer/history/BazaarTagParser.java | 11 +- .../history/BitKeeperHistoryParser.java | 24 +- .../indexer/history/BitKeeperTagParser.java | 5 +- .../indexer/history/FileAnnotationCache.java | 1 + .../indexer/history/GitRepository.java | 9 +- .../org/opengrok/indexer/history/History.java | 4 +- .../opengrok/indexer/history/HistoryGuru.java | 4 +- .../opengrok/indexer/history/Repository.java | 6 +- .../indexer/index/NumLinesLOCAccessor.java | 2 +- .../indexer/index/NumLinesLOCAggregator.java | 9 +- .../indexer/search/CustomQueryParser.java | 3 +- .../opengrok/indexer/search/QueryBuilder.java | 3 +- .../indexer/search/SettingsHelper.java | 8 +- .../indexer/search/TermEscaperBase.java | 2 +- .../indexer/search/context/Context.java | 19 +- .../search/context/HistoryContext.java | 8 +- .../indexer/search/context/PhraseMatcher.java | 2 - .../org/opengrok/indexer/util/ColorUtil.java | 2 +- .../org/opengrok/indexer/util/CtagsUtil.java | 9 +- .../org/opengrok/indexer/util/Executor.java | 43 +-- .../org/opengrok/indexer/util/Getopt.java | 6 +- .../opengrok/indexer/util/HeadHandler.java | 6 +- .../org/opengrok/indexer/util/HostUtil.java | 2 +- .../opengrok/indexer/util/LineBreaker.java | 8 +- .../org/opengrok/indexer/util/NullWriter.java | 3 + .../opengrok/indexer/util/OptionParser.java | 24 +- .../org/opengrok/indexer/util/PathUtils.java | 11 +- .../org/opengrok/indexer/util/Progress.java | 20 +- .../indexer/util/RainbowColorGenerator.java | 2 +- .../opengrok/indexer/util/SourceSplitter.java | 12 +- .../opengrok/indexer/util/TandemFilename.java | 1 + .../indexer/util/WrapperIOException.java | 56 +++ .../opengrok/indexer/web/EftarFileReader.java | 14 +- .../org/opengrok/indexer/web/Laundromat.java | 87 ++--- .../java/org/opengrok/indexer/web/Prefix.java | 10 +- .../opengrok/indexer/web/SearchHelper.java | 34 +- .../java/org/opengrok/indexer/web/Util.java | 5 +- .../PositiveDurationValidator.java | 2 +- .../web/messages/MessagesContainer.java | 4 +- .../indexer/web/messages/MessagesUtils.java | 2 +- .../indexer/analysis/AnalyzerGuruTest.java | 4 +- .../indexer/analysis/XrefTestBase.java | 2 +- .../yaml/YamlSymbolTokenizerTest.java | 2 +- .../indexer/analysis/yaml/YamlXrefTest.java | 2 +- .../opengrok/indexer/web/LaundromatTest.java | 4 +- .../opengrok/suggest/ComplexQueryData.java | 37 ++ .../suggest/SuggestResultCollector.java | 131 +++++++ .../suggest/SuggesterProjectData.java | 66 ++-- .../opengrok/suggest/SuggesterSearcher.java | 109 ++---- .../impl/chronicle/BytesRefDataAccess.java | 11 +- .../impl/chronicle/BytesRefSizedReader.java | 11 +- .../customized/CustomExactPhraseScorer.java | 34 +- .../customized/CustomSloppyPhraseScorer.java | 8 +- 69 files changed, 1015 insertions(+), 758 deletions(-) create mode 100644 opengrok-indexer/src/main/java/org/opengrok/indexer/util/WrapperIOException.java create mode 100644 suggester/src/main/java/org/opengrok/suggest/ComplexQueryData.java create mode 100644 suggester/src/main/java/org/opengrok/suggest/SuggestResultCollector.java diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/AnalyzerGuru.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/AnalyzerGuru.java index 895a2c3742c..a37cc08233d 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/AnalyzerGuru.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/AnalyzerGuru.java @@ -123,6 +123,7 @@ import org.opengrok.indexer.search.QueryBuilder; import org.opengrok.indexer.util.IOUtils; import org.opengrok.indexer.util.Statistics; +import org.opengrok.indexer.util.WrapperIOException; import org.opengrok.indexer.web.Util; /** @@ -194,7 +195,7 @@ public class AnalyzerGuru { */ private static final Comparator descStrlenComparator = (s1, s2) -> { // DESC: s2 length <=> s1 length - int cmp = Integer.compare(s2.length(), s1.length()); + var cmp = Integer.compare(s2.length(), s1.length()); if (cmp != 0) { return cmp; } @@ -261,7 +262,7 @@ public class AnalyzerGuru { new IgnorantAnalyzerFactory(), new BZip2AnalyzerFactory(), new XMLAnalyzerFactory(), - YamlAnalyzerFactory.DEFAULT_INSTANCE, + new YamlAnalyzerFactory(), MandocAnalyzerFactory.DEFAULT_INSTANCE, TroffAnalyzerFactory.DEFAULT_INSTANCE, new ELFAnalyzerFactory(), @@ -552,7 +553,7 @@ public static AbstractAnalyzer getAnalyzer(String fileTypeName) { */ public static AbstractAnalyzer getAnalyzer(InputStream in, String file) throws IOException { AnalyzerFactory factory = find(in, file); - if (factory == null) { + if (Objects.isNull(factory)) { AbstractAnalyzer defaultAnalyzer = getAnalyzer(); if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.log(Level.FINEST, "''{0}'': fallback {1}", @@ -616,7 +617,7 @@ public void populateDocument(Document doc, File file, String path, AbstractAnaly */ File fpath = new File(path); String fileParent = fpath.getParent(); - if (fileParent != null && fileParent.length() > 0) { + if (fileParent != null && !fileParent.isEmpty()) { String normalizedPath = QueryBuilder.normalizeDirPath(fileParent); StringField npstring = new StringField(QueryBuilder.DIRPATH, normalizedPath, Store.NO); doc.add(npstring); @@ -704,7 +705,7 @@ public static void writeXref(AnalyzerFactory factory, Reader in, * @param file file object, used for logging only * @throws java.io.IOException if an error occurs while creating the output */ - @SuppressWarnings("java:S5443") + @SuppressWarnings({"java:S5443", "java:S107"}) public static void writeDumpedXref(String contextPath, AnalyzerFactory factory, Reader in, Writer out, @Nullable Definitions defs, Annotation annotation, Project project, File file) throws IOException { @@ -902,46 +903,60 @@ public static AnalyzerFactory find(InputStream in, String file) throws IOExcepti * @return the analyzer factory to use */ public static AnalyzerFactory find(String file) { - String path = file; - int i; + String path; + var i = file.lastIndexOf(File.separatorChar); // Get basename of the file first. - if (((i = path.lastIndexOf(File.separatorChar)) > 0) - && (i + 1 < path.length())) { - path = path.substring(i + 1); + if (i > 0 && (i + 1 < file.length())) { + path = file.substring(i + 1); + } else { + path = file; } - int dotpos = path.lastIndexOf('.'); - if (dotpos >= 0) { - AnalyzerFactory factory; - - // Try matching the prefix. - if (dotpos > 0) { - factory = pre.get(path.substring(0, dotpos).toUpperCase(Locale.ROOT)); - if (factory != null) { - if (LOGGER.isLoggable(Level.FINEST)) { - LOGGER.log(Level.FINEST, "''{0}'': chosen by prefix: {1}", - new Object[]{file, factory.getClass().getSimpleName()}); - } - return factory; - } - } - - // Now try matching the suffix. We kind of consider this order (first + var dotPos = path.lastIndexOf('.'); + Optional optionalFactory = Optional.empty(); + if (dotPos >= 0) { + // Try matching the prefix and then by suffix. + // We kind of consider this order (first // prefix then suffix) to be workable although for sure there can be // cases when this does not work. - factory = ext.get(path.substring(dotpos + 1).toUpperCase(Locale.ROOT)); - if (factory != null) { - if (LOGGER.isLoggable(Level.FINEST)) { - LOGGER.log(Level.FINEST, "''{0}'': chosen by suffix: {1}", - new Object[]{file, factory.getClass().getSimpleName()}); - } - return factory; - } + optionalFactory = findByPrefix(file, path, dotPos) + .or(() -> findBySuffix(file, path, dotPos)); + } // file doesn't have any of the prefix or extensions we know, try full match - return FILE_NAMES.get(path.toUpperCase(Locale.ROOT)); + return optionalFactory + .orElseGet(() -> FILE_NAMES.get(path.toUpperCase(Locale.ROOT))); + } + + private static Optional findBySuffix(String file, String path, int dotPos) { + var suffixFactory = Optional.of(dotPos) + .map(pos -> path.substring(dotPos + 1)) + .map(suffix -> suffix.toUpperCase(Locale.ROOT)) + .map(ext::get); + suffixFactory.ifPresent(factory -> { + if (LOGGER.isLoggable(Level.FINEST)) { + LOGGER.log(Level.FINEST, "''{0}'': chosen by suffix: {1}", + new Object[]{file, factory.getClass().getSimpleName()}); + } + }); + return suffixFactory; + } + + private static Optional findByPrefix(String file, String path, int dotPos) { + var prefixFactory = Optional.of(dotPos) + .map(pos -> path.substring(0, dotPos)) + .filter(prefix -> !prefix.isEmpty()) + .map(prefix -> prefix.toUpperCase(Locale.ROOT)) + .map(pre::get); + prefixFactory.ifPresent(factory -> { + if (LOGGER.isLoggable(Level.FINEST)) { + LOGGER.log(Level.FINEST, "''{0}'': chosen by prefix: {1}", + new Object[]{file, factory.getClass().getSimpleName()}); + } + }); + return prefixFactory; } /** @@ -973,98 +988,90 @@ private static AnalyzerFactory findForStream(InputStream in, int len = in.read(content); in.reset(); - if (len < MAGIC_BYTES_NUM) { - /* - * Need at least 4 bytes to perform magic string matching. - */ - if (len < 4) { - return null; - } - content = Arrays.copyOf(content, len); - } - - AnalyzerFactory fac; - - // First, do precise-magic Matcher matching - for (FileAnalyzerFactory.Matcher matcher : matchers) { - if (matcher.isPreciseMagic()) { - fac = matcher.isMagic(content, in); - if (fac != null) { - if (LOGGER.isLoggable(Level.FINEST)) { - LOGGER.log(Level.FINEST, - "''{0}'': chosen by precise magic: {1}", - new Object[]{file, fac.getClass().getSimpleName()}); - } - return fac; - } - } + /* + * Need at least 4 bytes to perform magic string matching. + */ + if (len < 4) { + return null; } - - // Next, look for magic strings - String opening = readOpening(in, content); - fac = findMagicString(opening, file); - if (fac != null) { - return fac; + var finalContent = Arrays.copyOf(content, len); + try { + return findFactoryUsingMatcher(finalContent, in, file, true) + .or(() -> findUsingMagicString(finalContent, in, file)) + .or(() -> findFactoryUsingMatcher(finalContent, in, file, false)) + .orElse(null); + } catch (WrapperIOException ex) { + throw ex.getParentIOException(); } + } - // Last, do imprecise-magic Matcher matching - for (FileAnalyzerFactory.Matcher matcher : matchers) { - if (!matcher.isPreciseMagic()) { - fac = matcher.isMagic(content, in); - if (fac != null) { - if (LOGGER.isLoggable(Level.FINEST)) { - LOGGER.log(Level.FINEST, - "''{0}'': chosen by imprecise magic: {1}", - new Object[]{file, fac.getClass().getSimpleName()}); + private static Optional findFactoryUsingMatcher(byte[] content, InputStream in, + String file, + boolean isPrecise) { + var optionalFactory = matchers.stream() + .filter(matcher -> isPrecise == matcher.isPreciseMagic()) + .map(matcher -> { + try { + return matcher.isMagic(content, in); + } catch (IOException e) { + throw new WrapperIOException(e); } - return fac; - } + }) + .filter(Objects::nonNull) + .findFirst(); + + optionalFactory.ifPresent(factory -> { + if (LOGGER.isLoggable(Level.FINEST)) { + LOGGER.log(Level.FINEST, + "''{0}'': chosen by {1} magic: {2}", + new Object[]{file, isPrecise ? "precise" : "imprecise", + factory.getClass().getSimpleName()}); } - } + }); + return optionalFactory; - return null; } - private static AnalyzerFactory findMagicString(String opening, String file) { + private static Optional findUsingMagicString(byte[] content, InputStream in, String file) { + String opening; + try { + opening = readOpening(in, content); + } catch (IOException e) { + throw new WrapperIOException(e); + } // first, try to look up two words in magics String fragment = getWords(opening, 2); AnalyzerFactory fac = magics.get(fragment); - if (fac != null) { - if (LOGGER.isLoggable(Level.FINEST)) { - LOGGER.log(Level.FINEST, "''{0}'': chosen by magic {2}: {1}", - new Object[]{file, fac.getClass().getSimpleName(), fragment}); - } - return fac; - } // second, try to look up one word in magics - fragment = getWords(opening, 1); - fac = magics.get(fragment); + if (Objects.isNull(fac)) { + fragment = getWords(opening, 1); + fac = magics.get(fragment); + } if (fac != null) { if (LOGGER.isLoggable(Level.FINEST)) { LOGGER.log(Level.FINEST, "''{0}'': chosen by magic {2}: {1}", new Object[]{file, fac.getClass().getSimpleName(), fragment}); } - return fac; + return Optional.of(fac); } - // try to match initial substrings (DESC strlen) - for (Map.Entry entry : - magics.entrySet()) { - String magic = entry.getKey(); - if (opening.startsWith(magic)) { - fac = entry.getValue(); - if (LOGGER.isLoggable(Level.FINEST)) { - LOGGER.log(Level.FINEST, - "''{0}'': chosen by magic(substr) {2}: {1}", - new Object[]{file, fac.getClass().getSimpleName(), magic}); - } - return fac; - } - } + return magics.entrySet() + .stream() + .filter(entry -> opening.startsWith(entry.getKey())) + .map(entry -> { + if (LOGGER.isLoggable(Level.FINEST)) { + LOGGER.log(Level.FINEST, + "''{0}'': chosen by magic(substr) {2}: {1}", + new Object[]{file, + entry.getValue().getClass().getSimpleName(), entry.getKey()} + ); + } + return entry.getValue(); + }) + .findFirst(); - return null; } /** @@ -1109,66 +1116,68 @@ private static String readOpening(InputStream in, byte[] sig) in.mark(MARK_READ_LIMIT); - String encoding = IOUtils.findBOMEncoding(sig); - if (encoding == null) { - // SRCROOT is read with UTF-8 as a default. - encoding = StandardCharsets.UTF_8.name(); - } else { - int skipForBOM = IOUtils.skipForBOM(sig); - if (in.skip(skipForBOM) < skipForBOM) { - in.reset(); - return ""; - } - } - - int nRead = 0; - boolean sawNonWhitespace = false; - boolean lastWhitespace = false; - boolean postHashbang = false; - int r; + var startingString = findAndSkipBomEncoding(in, sig) + .map(encoding -> readOpeningFromBomSkippedStream(in, encoding)) + .orElse(""); + in.reset(); + return startingString; + } - StringBuilder opening = new StringBuilder(); - BufferedReader readr = new BufferedReader(new InputStreamReader(in, encoding), OPENING_MAX_CHARS); - while ((r = readr.read()) != -1) { - if (++nRead > OPENING_MAX_CHARS) { - break; - } - char c = (char) r; - boolean isWhitespace = Character.isWhitespace(c); - if (!sawNonWhitespace) { - if (isWhitespace) { - continue; + private static String readOpeningFromBomSkippedStream(InputStream in, String encoding) { + var opening = new StringBuilder(); + try { + var readr = new BufferedReader(new InputStreamReader(in, encoding), OPENING_MAX_CHARS); + var ignoreWhiteSpace = true; + var breakOnNewLine = false; + for (int nRead = 0, r = readr.read(); r != -1 && nRead <= OPENING_MAX_CHARS; + r = readr.read(), nRead++) { + + var c = (char) r; + if (breakOnNewLine && c == '\n') { + break; } - sawNonWhitespace = true; - } - if (c == '\n') { - break; - } + if (Character.isWhitespace(c)) { + if (!ignoreWhiteSpace) { + opening.append(' '); + ignoreWhiteSpace = true; + } + } else { + opening.append(c); + // If the opening starts with "#!", then track so that any + // trailing whitespace after the hashbang is ignored. + ignoreWhiteSpace = opening.length() == 2 && opening.charAt(0) == '#' && opening.charAt(1) == '!'; + breakOnNewLine = true; - if (isWhitespace) { - // Track `lastWhitespace' to condense stretches of whitespace, - // and use ' ' regardless of actual whitespace character to - // accord with magic string definitions. - if (!lastWhitespace && !postHashbang) { - opening.append(' '); } - } else { - opening.append(c); - postHashbang = false; - } - lastWhitespace = isWhitespace; - - // If the opening starts with "#!", then track so that any - // trailing whitespace after the hashbang is ignored. - if (opening.length() == 2 && opening.charAt(0) == '#' && opening.charAt(1) == '!') { - postHashbang = true; } + } catch (IOException e) { + throw new WrapperIOException(e); } - in.reset(); return opening.toString(); } + /** + * Identify the BOM encoding or default to utf 8, skips the encoding bytes . + * Empty optional is returned if all bom bytes are not skipped + * @param in The input stream containing the data + * @param sig The initial sequence of bytes in the input stream + * @return optional of encoding + * @throws IOException in case of any read error + */ + private static Optional findAndSkipBomEncoding(InputStream in, byte[] sig) throws IOException { + var encoding = IOUtils.findBOMEncoding(sig); + if (Objects.isNull(encoding)) { + // SRCROOT is read with UTF-8 as a default. + encoding = StandardCharsets.UTF_8.name(); + } + var skipForBOM = IOUtils.skipForBOM(sig); + if (skipForBOM != 0 && in.skip(skipForBOM) < skipForBOM) { + return Optional.empty(); + } + return Optional.of(encoding); + } + private static void addCustomizationKey(String k) { CUSTOMIZATION_KEYS.add(k); Object[] keys = CUSTOMIZATION_KEYS.toArray(); @@ -1195,4 +1204,6 @@ private static boolean factoriesDifferent(AnalyzerFactory a, AnalyzerFactory b) } return aName == null || !aName.equals(bName); } + + } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/Ctags.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/Ctags.java index 1e7ec8c1edf..af942d22a93 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/Ctags.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/Ctags.java @@ -574,6 +574,7 @@ public int exitValue() { @Override public void destroy() { + //Empty Method } }; diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/CtagsReader.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/CtagsReader.java index 1e2c3812dfe..11ac65058ca 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/CtagsReader.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/CtagsReader.java @@ -258,11 +258,9 @@ public void readLine(String tagLine) { addTag(defs, cidx.lineno, def, type, match, classInher, signature, cidx.lineStart, cidx.lineEnd); - String[] args; if (signature != null && !signature.equals("()") && - !signature.startsWith("() ") && (args = - splitSignature(signature)) != null) { - for (String arg : args) { + !signature.startsWith("() ") ) { + for (String arg : splitSignature(signature)) { //TODO this algorithm assumes that data types occur to // the left of the argument name, so it will not // work for languages like rust, kotlin, etc. which @@ -287,7 +285,7 @@ public void readLine(String tagLine) { arg = a[0]; // throws away assigned value } arg = arg.trim(); - if (arg.length() < 1) { + if (arg.isEmpty()) { continue; } @@ -433,7 +431,7 @@ private CpatIndex bestIndexOfTag(int lineno, String whole, String str) { * @return a defined instance */ private CpatIndex bestIndexOfArg(int lineno, String whole, String arg) { - if (whole.length() < 1) { + if (whole.isEmpty()) { return new CpatIndex(lineno, 0, 1, true); } @@ -551,9 +549,9 @@ private PatResult bestMatch(String whole, String arg, Pattern argpat) { * ending with a word character. */ private int strictIndexOf(String whole, String substr) { - boolean strictLeft = substr.length() > 0 && WORD_CHAR.matcher( + boolean strictLeft = !substr.isEmpty() && WORD_CHAR.matcher( String.valueOf(substr.charAt(0))).matches(); - boolean strictRight = substr.length() > 0 && WORD_CHAR.matcher( + boolean strictRight = !substr.isEmpty() && WORD_CHAR.matcher( String.valueOf(substr.charAt(substr.length() - 1))).matches(); int spos = 0; @@ -703,7 +701,7 @@ private static String[] splitSignature(String signature) { int soff = off0; int eoff = offz; if (soff >= eoff) { - return null; + return new String[0]; } // Trim outer punctuation if it exists. diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/Definitions.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/Definitions.java index 714844a2444..1e02370a61e 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/Definitions.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/Definitions.java @@ -23,6 +23,7 @@ */ package org.opengrok.indexer.analysis; +import org.jetbrains.annotations.Nullable; import org.opengrok.indexer.util.WhitelistObjectInputFilter; import java.io.ByteArrayInputStream; @@ -33,11 +34,14 @@ import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; public class Definitions implements Serializable { @@ -60,14 +64,14 @@ public class Definitions implements Serializable { public static class LineTagMap implements Serializable { private static final long serialVersionUID = 1191703801007779481L; - private final Map> sym_tags; //NOPMD + private final Map> symTags; //NOPMD protected LineTagMap() { - this.sym_tags = new HashMap<>(); + this.symTags = new HashMap<>(); } } // line number -> tag map - private final Map line_maps; + private final Map lineMaps; /** * Map from symbol to the line numbers on which the symbol is defined. @@ -80,7 +84,7 @@ protected LineTagMap() { public Definitions() { symbols = new HashMap<>(); - line_maps = new HashMap<>(); + lineMaps = new HashMap<>(); tags = new ArrayList<>(); } @@ -121,16 +125,18 @@ public boolean hasSymbol(String symbol) { * @return {@code true} if {@code symbol} is defined on the specified line */ public boolean hasDefinitionAt(String symbol, int lineNumber, String[] strs) { - Set lines = symbols.get(symbol); if (strs.length > 0) { strs[0] = "none"; } // Get tag info - if (lines != null && lines.contains(lineNumber)) { - LineTagMap lineMap = line_maps.get(lineNumber); + boolean isDefinitionPresent = Optional.ofNullable(symbols.get(symbol)) + .filter(lines -> lines.contains(lineNumber)) + .isPresent(); + if (isDefinitionPresent) { + LineTagMap lineMap = lineMaps.get(lineNumber); if (lineMap != null) { - for (Tag tag : lineMap.sym_tags.get(symbol)) { + for (Tag tag : lineMap.symTags.get(symbol)) { if (tag.used) { continue; } @@ -180,20 +186,15 @@ public List getTags() { * Get a list of all tags on given line. * * @param line line number - * @return list of tags + * @return list of tags or null */ - public List getTags(int line) { - LineTagMap lineMap = line_maps.get(line); - List result = null; - - if (lineMap != null) { - result = new ArrayList<>(); - for (Set ltags : lineMap.sym_tags.values()) { - result.addAll(ltags); - } - } - - return result; + public @Nullable List getTags(int line) { + return Optional.ofNullable(lineMaps.get(line)) + .map(lineMap -> lineMap.symTags.values().stream() + .flatMap(Collection::stream) + .collect(Collectors.toList()) + ) + .orElse(null); } /** @@ -267,26 +268,20 @@ public void addTag(int line, String symbol, String type, String text, Tag newTag = new Tag(line, symbol, type, text, namespace, signature, lineStart, lineEnd); tags.add(newTag); - Set lines = symbols.get(symbol); - if (lines == null) { - lines = new HashSet<>(); - symbols.put(symbol, lines); - } + Set lines = symbols.computeIfAbsent(symbol, + k -> new HashSet<>()); Integer aLine = line; lines.add(aLine); // Get per line map - LineTagMap lineMap = line_maps.get(aLine); - if (lineMap == null) { - lineMap = new LineTagMap(); - line_maps.put(aLine, lineMap); - } + LineTagMap lineMap = lineMaps.computeIfAbsent(aLine, + key -> new LineTagMap()); // Insert sym->tag map for this line - Set ltags = lineMap.sym_tags.get(symbol); + Set ltags = lineMap.symTags.get(symbol); if (ltags == null) { ltags = new HashSet<>(); - lineMap.sym_tags.put(symbol, ltags); + lineMap.symTags.put(symbol, ltags); } ltags.add(newTag); } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/JFlexXrefUtils.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/JFlexXrefUtils.java index 46493485e6c..97692feec4d 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/JFlexXrefUtils.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/JFlexXrefUtils.java @@ -26,11 +26,12 @@ import java.io.IOException; import java.io.Writer; +import java.util.Collection; import java.util.Comparator; import java.util.HashMap; -import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; @@ -55,6 +56,7 @@ public class JFlexXrefUtils { * Matches an HTML 5 ID or Name. */ private static final Pattern HTML5_ID_NAME = Pattern.compile("(?U)^\\S+$"); + private static final String A_HREF_START = ""); Util.htmlize(url, out); @@ -166,16 +168,13 @@ public static Scope maybeNewScope(boolean scopesEnabled, Scope existingScope, JFlexStackingLexer lexer, Definitions defs) { if (scopesEnabled && existingScope == null) { int line = lexer.getLineNumber(); - if (defs != null) { - List tags = defs.getTags(line); - if (tags != null) { - for (Tag tag : tags) { - if (tag.type.startsWith("function") || tag.type.startsWith("method")) { - return new Scope(tag.line, tag.line, tag.symbol, tag.namespace, tag.signature); - } - } - } - } + return Optional.ofNullable(defs) + .map(objDefs -> objDefs.getTags(line)) + .stream().flatMap(Collection::stream) + .filter(tag -> tag.type.startsWith("function") || tag.type.startsWith("method")) + .findFirst() + .map(tag -> new Scope(tag.line, tag.line, tag.symbol, tag.namespace, tag.signature)) + .orElse(null); } return null; } @@ -234,11 +233,11 @@ public static boolean writeSymbol(Writer out, Definitions defs, if (defs != null && defs.hasDefinitionAt(symbol, line, strs)) { // This is the definition of the symbol. String type = strs[0]; - String style_class = "d"; + String styleClass = "d"; XrefStyle style = XrefStyle.getStyle(type); if (style != null) { - style_class = style.ssClass; + styleClass = style.ssClass; } // 1) Create an anchor for direct links. (Perhaps we should only @@ -255,20 +254,20 @@ public static boolean writeSymbol(Writer out, Definitions defs, // explained by @tulinkry. if (HTML5_ID_NAME.matcher(symbol).matches()) { out.append(""); } // 2) Create a link that searches for all references to this symbol. - out.append(""); @@ -281,7 +280,7 @@ public static boolean writeSymbol(Writer out, Definitions defs, // that is defined more than once in this file. In either case, we // can't generate a direct link to the definition, so generate a // link to search for all definitions of that symbol instead. - out.append(" data; - public HCLLexer() { + protected HCLLexer() { dataHead = new HCLLexerData(); } @@ -113,7 +113,7 @@ public void hereOp(String capture) throws IOException { * @return true if a Here state was pushed */ public boolean maybeHereStart() throws IOException { - if (dataHead.hereSettings != null && dataHead.hereSettings.size() > 0) { + if (dataHead.hereSettings != null && !dataHead.hereSettings.isEmpty()) { HereDocSettings settings = dataHead.hereSettings.peek(); yypush(settings.state); disjointSpan(HtmlConsts.STRING_CLASS); @@ -141,7 +141,7 @@ public boolean maybeHereEnd(String capture) throws IOException { offer(capture); - if (dataHead.hereSettings.size() > 0) { + if (!dataHead.hereSettings.isEmpty()) { settings = dataHead.hereSettings.peek(); yybegin(settings.state); if (didZspan) { diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/yaml/YamlAnalyzerFactory.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/yaml/YamlAnalyzerFactory.java index 304bcd5b788..e18591d4ff5 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/yaml/YamlAnalyzerFactory.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/analysis/yaml/YamlAnalyzerFactory.java @@ -35,11 +35,8 @@ public class YamlAnalyzerFactory extends FileAnalyzerFactory { "YML" }; - public static final YamlAnalyzerFactory DEFAULT_INSTANCE = - new YamlAnalyzerFactory(); - - private YamlAnalyzerFactory() { + public YamlAnalyzerFactory() { super(null, null, SUFFIXES, null, null, "text/plain", AbstractAnalyzer.Genre.PLAIN, NAME); } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/ConfigMerge.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/ConfigMerge.java index 7a7bc54743d..66f83e61c13 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/ConfigMerge.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/ConfigMerge.java @@ -32,6 +32,10 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.text.ParseException; +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.Collectors; + import org.opengrok.indexer.util.Getopt; /** @@ -40,6 +44,7 @@ * * @author Vladimir Kotal */ +@SuppressWarnings("java:S106") public class ConfigMerge { private static final String NAME = "ConfigMerge"; @@ -58,13 +63,12 @@ public static void merge(Configuration cfgBase, Configuration cfgNew) throws Exc // Basic strategy: take all non-static/transient fields that have a setter // from cfgBase that are not of default value and set them to cfgNew. - for (Field field : cfgBase.getClass().getDeclaredFields()) { + var fields = Arrays.stream(cfgBase.getClass().getDeclaredFields()) + .filter(ConfigMerge::isCopyable) + .collect(Collectors.toList()); + + for (Field field : fields) { String fieldName = field.getName(); - int modifiers = field.getModifiers(); - if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers) || - Modifier.isFinal(modifiers)) { - continue; - } PropertyDescriptor desc; try { desc = new PropertyDescriptor(fieldName, Configuration.class); @@ -84,8 +88,7 @@ public static void merge(Configuration cfgBase, Configuration cfgNew) throws Exc try { Object obj = getter.invoke(cfgBase); - if ((obj == null && getter.invoke(cfgDefault) == null) || - obj.equals(getter.invoke(cfgDefault))) { + if (Objects.equals(obj, getter.invoke(cfgDefault))) { continue; } } catch (Exception ex) { @@ -100,6 +103,13 @@ public static void merge(Configuration cfgBase, Configuration cfgNew) throws Exc } } + private static boolean isCopyable(Field field) { + + int modifiers = field.getModifiers(); + return !Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers) && + !Modifier.isFinal(modifiers); + } + public static void main(String[] argv) { Getopt getopt = new Getopt(argv, "h?"); diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/ConfigurationHelp.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/ConfigurationHelp.java index 15f5edb4107..22c67c65206 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/ConfigurationHelp.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/ConfigurationHelp.java @@ -32,8 +32,12 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.TreeMap; + +import org.jetbrains.annotations.Nullable; import org.opengrok.indexer.authorization.AuthControlFlag; import org.opengrok.indexer.authorization.AuthorizationPlugin; import org.opengrok.indexer.authorization.AuthorizationStack; @@ -194,14 +198,14 @@ private static Object getSampleValue(Method setter, Object defaultValue) { } } - private static Object getSampleListValue(Type genType) { - if (!(genType instanceof ParameterizedType)) { - return null; - } - ParameterizedType genParamType = (ParameterizedType) genType; - Type actType = genParamType.getActualTypeArguments()[0]; + private static Object getSampleListValue(@Nullable Type genType) { + var actType = Optional.ofNullable(genType) + .filter(ParameterizedType.class::isInstance) + .map(ParameterizedType.class::cast) + .map(genParamType -> genParamType.getActualTypeArguments()[0]) + .orElse(null); - if (actType != RepositoryInfo.class) { + if (Objects.nonNull(actType) && actType != RepositoryInfo.class) { throw new UnsupportedOperationException(NOT_SUPPORTED_MSG + actType); } return null; @@ -300,22 +304,21 @@ private static Object getDefaultValue(Class klass, Method setter, return null; } } - - // Return a text override for some objects. - switch (gname) { - case "getSuggesterConfig": - return "as below but with Boolean opposites, non-zeroes decremented by 1, null " + - "for allowed-projects, and also including \"full\" in allowed-fields"; - case "getPluginStack": - return "an empty stack"; - case "getIncludedNames": - return "an empty filter"; - case "getIgnoredNames": - return "OpenGrok's standard set of ignored files and directories"; - } - try { - return getter.invoke(cinst); + // Return a text override for some objects. + switch (gname) { + case "getSuggesterConfig": + return "as below but with Boolean opposites, non-zeroes decremented by 1, null " + + "for allowed-projects, and also including \"full\" in allowed-fields"; + case "getPluginStack": + return "an empty stack"; + case "getIncludedNames": + return "an empty filter"; + case "getIgnoredNames": + return "OpenGrok's standard set of ignored files and directories"; + default: + return getter.invoke(cinst); + } } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { return null; } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/Filter.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/Filter.java index 2b9a1cb9852..235a6452c10 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/Filter.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/Filter.java @@ -112,31 +112,24 @@ public boolean match(File file, boolean checkAbsolute) { if (filenames.contains(fileName)) { return true; } - - for (Pattern p : patterns) { - // Try to match the basename first. - Matcher m = p.matcher(fileName); - if (m.matches()) { - return true; - } - // Try the full path next. - if (checkAbsolute && p.pattern().contains("/")) { - m = p.matcher(absolute); - if (m.matches()) { - return true; - } - } + // Try to match the basename first. + var matchFound = patterns.stream() + .map(p -> p.matcher(fileName)) + .anyMatch(Matcher::matches); + // Try the full path next. + if (!matchFound) { + matchFound = patterns.stream() + .filter(p -> checkAbsolute && p.pattern().contains("/")) + .map(p -> p.matcher(absolute)) + .anyMatch(Matcher::matches); } - - if (checkAbsolute) { - for (String path : paths) { - if (absolute.endsWith(path)) { - return true; - } - } + if (!matchFound) { + matchFound = paths.stream() + .filter(path -> checkAbsolute) + .anyMatch(absolute::endsWith); } - return false; + return matchFound; } /** @@ -194,7 +187,7 @@ private Pattern compilePattern(String pattern) { int pos = 0; String[] components = pattern.split("[*?]"); for (String str : components) { - if (str.length() > 0) { + if (!str.isEmpty()) { // Quote the characters up to next wildcard or end of string. regex.append(Pattern.quote(str)); pos += str.length(); diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/Group.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/Group.java index fcc76ae1fd3..43068687120 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/Group.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/Group.java @@ -40,6 +40,7 @@ * @author Krystof Tulinger * @version $Revision$ */ +@SuppressWarnings("java:S2065") public class Group implements Comparable, Nameable { static { @@ -273,15 +274,7 @@ public boolean equals(Object obj) { } final Group other = (Group) obj; - int numNull = (name == null ? 1 : 0) + (other.name == null ? 1 : 0); - switch (numNull) { - case 0: - return name.equals(other.name); - case 1: - return false; - default: - return true; - } + return Objects.equals(name, other.name); } /** diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/IndexTimestamp.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/IndexTimestamp.java index 1ee157f215d..305105277ae 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/IndexTimestamp.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/IndexTimestamp.java @@ -30,7 +30,7 @@ import org.opengrok.indexer.logger.LoggerFactory; public class IndexTimestamp { - private transient Date lastModified; + private Date lastModified; private static final Logger LOGGER = LoggerFactory.getLogger(IndexTimestamp.class); diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/framework/PluginClassLoader.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/framework/PluginClassLoader.java index 9bbf174c26b..ae9185d0e54 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/framework/PluginClassLoader.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/framework/PluginClassLoader.java @@ -30,6 +30,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.logging.Level; @@ -76,6 +77,7 @@ public PluginClassLoader(File directory) { this.directory = directory; } + @SuppressWarnings("java:S1181") private Class loadClassFromJar(String classname) throws ClassNotFoundException { File[] jars = directory.listFiles((dir, name) -> name.endsWith(".jar")); @@ -111,6 +113,7 @@ private Class loadClassFromJar(String classname) throws ClassNotFoundExceptio throw new ClassNotFoundException("Class \"" + classname + "\" could not be found"); } + @SuppressWarnings("java:S1181") private Class loadClassFromFile(String classname) throws ClassNotFoundException { try { String filename = classname.replace('.', File.separatorChar) + CLASS_SUFFIX; @@ -141,8 +144,8 @@ private byte[] loadBytes(InputStream in) throws IOException { private boolean checkWhiteList(String name) { for (String pattern : CLASS_WHITELIST) { - pattern = pattern.replaceAll("\\.", "\\\\."); - pattern = pattern.replaceAll("\\*", ".*"); + pattern = pattern.replace(".", "\\."); + pattern = pattern.replace("*", ".*"); if (name.matches(pattern)) { return true; } @@ -217,66 +220,60 @@ public Class loadClass(String name) throws ClassNotFoundException, SecurityEx */ @Override public Class loadClass(String name, boolean resolveIt) throws ClassNotFoundException, SecurityException { - Class c = cache.get(name); - if (c != null) { + var loadedClass = Optional.>ofNullable(cache.get(name)) + .or(() -> findAlreadyLoadedClass(name)) + .or(() -> findClassUsingParentClassLoader(name)) + .or(() -> findClassFromFile(name)) + .or(() -> findClassFromJar(name)); + loadedClass.ifPresent(clazz -> { + cache.put(name, clazz); if (resolveIt) { - resolveClass(c); + resolveClass(clazz); } - return c; - } - + }); + return loadedClass + .orElseThrow(() -> + new ClassNotFoundException("Class \"" + name + "\" was not found") + ); + } + private Optional> findAlreadyLoadedClass( String name) { checkClassname(name); + return Optional.ofNullable(findLoadedClass(name)); + } - // find already loaded class - if ((c = findLoadedClass(name)) != null) { - cache.put(name, c); - if (resolveIt) { - resolveClass(c); - } - return c; - } - - // try if parent classloader can load this class + private Optional> findClassUsingParentClassLoader( String name) { + Class clazz = null; if (this.getParent() != null) { try { - if ((c = this.getParent().loadClass(name)) != null) { - cache.put(name, c); - if (resolveIt) { - resolveClass(c); - } - return c; - } + clazz = this.getParent().loadClass(name); } catch (ClassNotFoundException ignored) { + //ignore Class Not Found Exception } } + return Optional.ofNullable(clazz); + } + private Optional> findClassFromFile( String name) { + Class clazz = null; try { checkPackage(name); - // load it from file - if ((c = loadClassFromFile(name)) != null) { - cache.put(name, c); - if (resolveIt) { - resolveClass(c); - } - return c; - } + clazz = loadClassFromFile(name); } catch (ClassNotFoundException ignored) { + //ignore Class Not Found Exception } + return Optional.ofNullable(clazz); + } + private Optional> findClassFromJar( String name) { + Class clazz = null; try { checkPackage(name); - // load it from jar - if ((c = loadClassFromJar(name)) != null) { - cache.put(name, c); - if (resolveIt) { - resolveClass(c); - } - return c; - } + clazz = loadClassFromJar(name); } catch (ClassNotFoundException ignored) { + //ignore Class Not Found Exception } - - throw new ClassNotFoundException("Class \"" + name + "\" was not found"); + return Optional.ofNullable(clazz); } + } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/framework/PluginFramework.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/framework/PluginFramework.java index 661d3b6a0b8..03e556d818b 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/framework/PluginFramework.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/framework/PluginFramework.java @@ -45,7 +45,7 @@ * * @author Krystof Tulinger */ -public abstract class PluginFramework { +public abstract class PluginFramework

{ private static final Logger LOGGER = LoggerFactory.getLogger(PluginFramework.class); @@ -54,7 +54,7 @@ public abstract class PluginFramework { /** * Class of the plugin type, necessary for instantiating and searching. */ - private final Class classType; + private final Class

classType; /** * Plugin directory. @@ -78,7 +78,7 @@ public abstract class PluginFramework { * @param classType the class of the plugin type * @param path the plugin directory path */ - protected PluginFramework(Class classType, String path) { + protected PluginFramework(Class

classType, String path) { this.classType = classType; setPluginDirectory(path); } @@ -168,7 +168,8 @@ protected String getClassName(IAuthorizationPlugin plugin) { * or null if there is no such class * @see #loadClass(String) */ - public PluginType handleLoadClass(String classname) { + @SuppressWarnings("java:S1181") + public P handleLoadClass(String classname) { try { return loadClass(classname); } catch (ClassNotFoundException ex) { @@ -205,7 +206,7 @@ public PluginType handleLoadClass(String classname) { * @throws InvocationTargetException if the underlying constructor of the class throws an exception */ @SuppressWarnings({"unchecked"}) - private PluginType loadClass(String classname) throws ClassNotFoundException, + private P loadClass(String classname) throws ClassNotFoundException, SecurityException, InstantiationException, IllegalAccessException, @@ -219,7 +220,7 @@ private PluginType loadClass(String classname) throws ClassNotFoundException, if (intf1.getCanonicalName().equals(classType.getCanonicalName()) && !Modifier.isAbstract(c.getModifiers())) { // call to non-parametric constructor - return (PluginType) c.getDeclaredConstructor().newInstance(); + return (P) c.getDeclaredConstructor().newInstance(); } } LOGGER.log(Level.FINEST, "Plugin class \"{0}\" does not implement IAuthorizationPlugin interface.", classname); @@ -255,7 +256,7 @@ protected List> getSuperclassesAndInterfaces(Class clazz) { * @see #loadClass(String) */ private void loadClassFiles(List fileList) { - PluginType plugin; + P plugin; for (File file : fileList) { String classname = getClassName(file); @@ -281,7 +282,7 @@ private void loadClassFiles(List fileList) { * @see #loadClass(String) */ private void loadJarFiles(List fileList) { - PluginType pf; + P pf; for (File file : fileList) { try (JarFile jar = new JarFile(file)) { @@ -348,7 +349,7 @@ private String getClassName(JarEntry jarEntry) { * * @param plugin the loaded plugin */ - protected abstract void classLoaded(PluginType plugin); + protected abstract void classLoaded(P plugin); /** diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/AccuRevHistoryParser.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/AccuRevHistoryParser.java index 29a22805a1a..ab25e33cbc0 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/AccuRevHistoryParser.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/AccuRevHistoryParser.java @@ -145,11 +145,11 @@ public void processStream(InputStream input) throws IOException { throw new IOException("Could not parse date: " + line, pe); } - } else if (line.startsWith(" #")) { // found comment + } else if (line.startsWith(" #") && (entry != null)) { // found comment entry.appendMessage(line.substring(3)); - } else if (line.startsWith(" v")) { // found version + } else if (line.startsWith(" v") && (entry != null)) { // found version String[] data = line.split("\\s+"); entry.setRevision(data[2]); diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/BazaarHistoryParser.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/BazaarHistoryParser.java index ee5c5b229a0..fbeba517f05 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/BazaarHistoryParser.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/BazaarHistoryParser.java @@ -37,6 +37,8 @@ import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; + +import org.jetbrains.annotations.NotNull; import org.opengrok.indexer.configuration.RuntimeEnvironment; import org.opengrok.indexer.logger.LoggerFactory; import org.opengrok.indexer.util.Executor; @@ -92,20 +94,18 @@ History parse(File file, String sinceRevision) throws HistoryException { */ @Override public void processStream(InputStream input) throws IOException { - RuntimeEnvironment env = RuntimeEnvironment.getInstance(); BufferedReader in = new BufferedReader(new InputStreamReader(input)); String s; - HistoryEntry entry = null; + var entry = createHistoryEntry(); int state = 0; while ((s = in.readLine()) != null) { if ("------------------------------------------------------------".equals(s)) { - if (entry != null && state > 2) { + if (state > 2) { entries.add(entry); } - entry = new HistoryEntry(); - entry.setActive(true); + entry = createHistoryEntry(); state = 0; continue; } @@ -113,83 +113,150 @@ public void processStream(InputStream input) throws IOException { switch (state) { case 0: // First, go on until revno is found. - if (s.startsWith("revno:")) { - String[] rev = s.substring("revno:".length()).trim().split(" "); - entry.setRevision(rev[0]); - ++state; - } + state = state + populateRevisionInHistoryEntry(s, entry); break; case 1: // Then, look for committer. - if (s.startsWith("committer:")) { - entry.setAuthor(s.substring("committer:".length()).trim()); - ++state; - } + state = state + populateCommitterInHistoryEntry(s, entry); break; case 2: // And then, look for timestamp. - if (s.startsWith("timestamp:")) { - try { - Date date = repository.parse(s.substring("timestamp:".length()).trim()); - entry.setDate(date); - } catch (ParseException e) { - // - // Overriding processStream() thus need to comply with the - // set of exceptions it can throw. - // - throw new IOException("Failed to parse history timestamp:" + s, e); - } - ++state; - } + state = state + populateDateInHistoryEntry(s, entry); break; case 3: - // Expect the commit message to follow immediately after - // the timestamp, and that everything up to the list of - // modified, added and removed files is part of the commit - // message. - if (s.startsWith("modified:") || s.startsWith("added:") || s.startsWith("removed:")) { - ++state; - } else if (s.startsWith(" ")) { - // Commit messages returned by bzr log -v are prefixed - // with two blanks. - entry.appendMessage(s.substring(2)); - } + // Expect the commit message to follow immediately after the timestamp + state = state + populateCommitMessageInHistoryEntry(s, entry); break; case 4: // Finally, store the list of modified, added and removed // files. (Except the labels.) - if (!(s.startsWith("modified:") || s.startsWith("added:") || s.startsWith("removed:"))) { - // The list of files is prefixed with blanks. - s = s.trim(); - - int idx = s.indexOf(" => "); - if (idx != -1) { - s = s.substring(idx + 4); - } - - File f = new File(myDir, s); - try { - String name = env.getPathRelativeToSourceRoot(f); - entry.addFile(name.intern()); - } catch (ForbiddenSymlinkException e) { - LOGGER.log(Level.FINER, e.getMessage()); - // ignored - } catch (InvalidPathException e) { - LOGGER.log(Level.WARNING, e.getMessage()); - } - } + populateCommitFilesInHistoryEntry(s, entry); break; default: LOGGER.log(Level.WARNING, "Unknown parser state: {0}", state); break; - } + } } - if (entry != null && state > 2) { + if (state > 2) { entries.add(entry); } } + private HistoryEntry createHistoryEntry() { + var entry = new HistoryEntry(); + entry.setActive(true); + return entry; + } + + /** + * + * @param currentLine currentLine of string from stream + * @param historyEntry current history entry to populate committer + */ + private void populateCommitFilesInHistoryEntry(@NotNull String currentLine, + @NotNull HistoryEntry historyEntry) throws IOException { + + if (!(currentLine.startsWith("modified:") || currentLine.startsWith("added:") + || currentLine.startsWith("removed:"))) { + // The list of files is prefixed with blanks. + currentLine = currentLine.trim(); + + int idx = currentLine.indexOf(" => "); + if (idx != -1) { + currentLine = currentLine.substring(idx + 4); + } + + File f = new File(myDir, currentLine); + try { + String name = RuntimeEnvironment.getInstance().getPathRelativeToSourceRoot(f); + historyEntry.addFile(name.intern()); + } catch (ForbiddenSymlinkException e) { + LOGGER.log(Level.FINER, e.getMessage()); + // ignored + } catch (InvalidPathException e) { + LOGGER.log(Level.WARNING, e.getMessage()); + } + } + } + + /** + * + * @param currentLine currentLine of string from stream + * @param historyEntry current history entry to populate committer + * @return 1 if committer is populated else 0 + */ + private int populateCommitMessageInHistoryEntry(@NotNull String currentLine, @NotNull HistoryEntry historyEntry) { + // everything up to the list of + // modified, added and removed files is part of the commit + // message. + int state = 0; + if (currentLine.startsWith("modified:") || currentLine.startsWith("added:") || currentLine.startsWith("removed:")) { + ++state; + } else if (currentLine.startsWith(" ")) { + // Commit messages returned by bzr log -v are prefixed + // with two blanks. + historyEntry.appendMessage(currentLine.substring(2)); + } + return state; + } + + /** + * + * @param currentLine currentLine of string from stream + * @param historyEntry current history entry to populate committer + * @return 1 if committer is populated else 0 + */ + private int populateCommitterInHistoryEntry(@NotNull String currentLine, @NotNull HistoryEntry historyEntry) { + int state = 0; + if (currentLine.startsWith("committer:")) { + historyEntry.setAuthor(currentLine.substring("committer:".length()).trim()); + ++state; + } + return state; + } + + /** + * + * @param currentLine currentLine of string from stream + * @param historyEntry current history entry to populate Date + * @return 1 if Date is populated else 0 + * @throws IOException on failure to parse timestamp + */ + private int populateDateInHistoryEntry(@NotNull String currentLine, @NotNull HistoryEntry historyEntry) throws IOException { + int state = 0; + if (currentLine.startsWith("timestamp:")) { + try { + Date date = repository.parse(currentLine.substring("timestamp:".length()).trim()); + historyEntry.setDate(date); + } catch (ParseException e) { + // + // Overriding processStream() thus need to comply with the + // set of exceptions it can throw. + // + throw new IOException("Failed to parse history timestamp:" + currentLine, e); + } + ++state; + } + return state; + } + + /** + * + * @param currentLine currentLine of string from stream + * @param historyEntry current history entry to populate Revision + * @return 1 if revision is populated else 0 + */ + private int populateRevisionInHistoryEntry(@NotNull String currentLine, @NotNull HistoryEntry historyEntry) { + int state = 0; + if (currentLine.startsWith("revno:")) { + String[] rev = currentLine.substring("revno:".length()).trim().split(" "); + historyEntry.setRevision(rev[0]); + ++state; + } + return state; + } + /** * Parse the given string. * diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/BazaarTagParser.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/BazaarTagParser.java index 6047f143359..7d58dbfc281 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/BazaarTagParser.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/BazaarTagParser.java @@ -29,7 +29,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.util.NavigableSet; import java.util.TreeSet; + import org.opengrok.indexer.util.Executor; /** @@ -41,14 +43,14 @@ public class BazaarTagParser implements Executor.StreamHandler { /** * Store tag entries created by processStream. */ - private final TreeSet entries = new TreeSet<>(); + private final NavigableSet entries = new TreeSet<>(); /** * Returns the set of entries that has been created. * * @return entries a set of tag entries */ - public TreeSet getEntries() { + public NavigableSet getEntries() { return entries; } @@ -63,10 +65,7 @@ public void processStream(InputStream input) throws IOException { } // Grrr, how to parse tags with spaces inside? // This solution will loose multiple spaces;-/ - String tag = parts[0]; - for (int i = 1; i < parts.length - 1; ++i) { - tag += " " + parts[i]; - } + var tag = String.join(" ", parts); TagEntry tagEntry = new BazaarTagEntry(Integer.parseInt(parts[parts.length - 1]), tag); // Bazaar lists multiple tags on more lines. We need to merge those into single TagEntry TagEntry higher = entries.ceiling(tagEntry); diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/BitKeeperHistoryParser.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/BitKeeperHistoryParser.java index 0fab214bd57..64ddb345e43 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/BitKeeperHistoryParser.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/BitKeeperHistoryParser.java @@ -101,14 +101,10 @@ public void processStream(InputStream input) throws IOException { final BufferedReader in = new BufferedReader(new InputStreamReader(input)); for (String line = in.readLine(); line != null; line = in.readLine()) { if (line.startsWith("D ")) { - if (entry != null) { - entries.add(entry); - entry = null; - } - - final String[] fields = line.substring(2).split("\t"); - final HistoryEntry newEntry = new HistoryEntry(); + entry = null; try { + final String[] fields = line.substring(2).split("\t"); + final HistoryEntry newEntry = new HistoryEntry(); if (fields[0].equals("ChangeSet")) { continue; } @@ -117,14 +113,13 @@ public void processStream(InputStream input) throws IOException { newEntry.setDate(dateFormat.parse(fields[2])); newEntry.setAuthor(fields[3]); newEntry.setActive(true); + entry = newEntry; + entries.add(entry); + if (fields.length == 5) { + renamedFiles.add(fields[4]); + } } catch (final Exception e) { LOGGER.log(Level.SEVERE, "Error: malformed BitKeeper log output {0}", line); - continue; - } - - entry = newEntry; - if (fields.length == 5) { - renamedFiles.add(fields[4]); } } else if (line.startsWith("C ") && (entry != null)) { final String messageLine = line.substring(2); @@ -132,8 +127,5 @@ public void processStream(InputStream input) throws IOException { } } - if (entry != null) { - entries.add(entry); - } } } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/BitKeeperTagParser.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/BitKeeperTagParser.java index 60a363990b7..49c469ec88a 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/BitKeeperTagParser.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/BitKeeperTagParser.java @@ -29,6 +29,7 @@ import java.io.InputStreamReader; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.NavigableSet; import java.util.TreeSet; import java.util.logging.Level; import java.util.logging.Logger; @@ -54,7 +55,7 @@ public class BitKeeperTagParser implements Executor.StreamHandler { /** * Store tag entries created by {@link #processStream(InputStream)}. */ - private final TreeSet entries = new TreeSet<>(); + private final NavigableSet entries = new TreeSet<>(); /** * Constructor to construct the thing to be constructed. @@ -70,7 +71,7 @@ public BitKeeperTagParser(String datePattern) { * * @return entries a set of tag entries */ - public TreeSet getEntries() { + public NavigableSet getEntries() { return entries; } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/FileAnnotationCache.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/FileAnnotationCache.java index 07701f5821d..f703ccb1835 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/FileAnnotationCache.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/FileAnnotationCache.java @@ -201,6 +201,7 @@ private void writeCache(AnnotationData annotationData, File outfile) throws IOEx mapper.writeValue(outfile, annotationData); } + @SuppressWarnings("java:S1764") public void store(File file, Annotation annotation) throws CacheException { if (annotation.getRevision() == null || annotation.getRevision().isEmpty()) { throw new CacheException(String.format("annotation for ''%s'' does not contain revision", file)); diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/GitRepository.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/GitRepository.java index 66046130dc3..310b27d3c0b 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/GitRepository.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/GitRepository.java @@ -36,6 +36,7 @@ import java.util.List; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.Scanner; import java.util.Set; import java.util.SortedSet; @@ -832,11 +833,9 @@ private RevCommit getCommit(org.eclipse.jgit.lib.Repository repository, Ref ref) @Nullable String determineParent(CommandTimeoutType cmdType) throws IOException { try (org.eclipse.jgit.lib.Repository repository = getJGitRepository(getDirectoryName())) { - if (repository.getConfig() != null) { - return repository.getConfig().getString("remote", Constants.DEFAULT_REMOTE_NAME, "url"); - } else { - return null; - } + return Optional.ofNullable(repository.getConfig()) + .map(config -> config.getString("remote", Constants.DEFAULT_REMOTE_NAME, "url")) + .orElse(null); } } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/History.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/History.java index 0c7ace80c66..dca8d97aa81 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/History.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/History.java @@ -24,7 +24,6 @@ package org.opengrok.indexer.history; import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.VisibleForTesting; import java.io.Serializable; import java.util.ArrayList; @@ -66,7 +65,6 @@ public History() { this(new ArrayList<>()); } - @VisibleForTesting public History(List entries) { this(entries, Collections.emptyList()); } @@ -111,7 +109,7 @@ public void setEntries(List entries) { * @param entries The entries to add to the list */ public void setHistoryEntries(List entries) { - this.entries = entries; + this.setEntries(entries); } /** diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/HistoryGuru.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/HistoryGuru.java index cd79fb3417d..320a293ee99 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/HistoryGuru.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/HistoryGuru.java @@ -1317,8 +1317,8 @@ public void invalidateRepositories(Collection repos, C } } catch (Exception ex) { // We want to catch any exception since we are in thread. - LOGGER.log(Level.WARNING, "Could not create " + repositoryInfo.getType() - + " repository object for '" + repositoryInfo.getDirectoryName() + "'", ex); + LOGGER.log(Level.WARNING, ex, () -> "Could not create " + repositoryInfo.getType() + + " repository object for '" + repositoryInfo.getDirectoryName() + "'"); } finally { latch.countDown(); progress.increment(); diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/Repository.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/Repository.java index 9dedd276ee3..f7584add142 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/history/Repository.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/history/Repository.java @@ -40,7 +40,7 @@ import java.util.Iterator; import java.util.List; import java.util.Locale; -import java.util.TreeSet; +import java.util.NavigableSet; import java.util.logging.Level; import java.util.logging.Logger; @@ -86,7 +86,7 @@ public abstract class Repository extends RepositoryInfo { * List of <revision, tags> pairs for repositories which display tags * only for files changed by the tagged commit. */ - protected TreeSet tagList = null; + protected NavigableSet tagList = null; abstract boolean fileHasHistory(File file); @@ -283,7 +283,7 @@ boolean hasFileBasedTags() { return false; } - TreeSet getTagList() { + NavigableSet getTagList() { return this.tagList; } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/index/NumLinesLOCAccessor.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/index/NumLinesLOCAccessor.java index 9787428ca6a..91cf2583fec 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/index/NumLinesLOCAccessor.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/index/NumLinesLOCAccessor.java @@ -79,7 +79,7 @@ public void store(IndexWriter writer, IndexReader reader, countsAggregator.iterator().forEachRemaining(counts::add); if (counts.size() >= BULK_READ_THRESHOLD) { storeBulk(writer, reader, counts, isAggregatingDeltas); - } else if (counts.size() > 0) { + } else if (!counts.isEmpty()) { storeIterative(writer, reader, counts, isAggregatingDeltas); } } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/index/NumLinesLOCAggregator.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/index/NumLinesLOCAggregator.java index 0c48786186f..c933f67c47b 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/index/NumLinesLOCAggregator.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/index/NumLinesLOCAggregator.java @@ -61,15 +61,12 @@ public void register(NumLinesLOC counts) { synchronized (syncRoot) { do { String dirPath = Util.fixPathIfWindows(directory.getPath()); - DeltaData extantDelta = registeredDeltas.get(dirPath); - if (extantDelta == null) { - extantDelta = new DeltaData(); - registeredDeltas.put(dirPath, extantDelta); - } + var extantDelta = registeredDeltas.computeIfAbsent(dirPath, + key -> new DeltaData()); extantDelta.numLines += counts.getNumLines(); extantDelta.loc += counts.getLOC(); } while ((directory = directory.getParentFile()) != null && - directory.getPath().length() > 0); + !directory.getPath().isEmpty()); } } } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/search/CustomQueryParser.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/search/CustomQueryParser.java index cb898fbf3c8..37765543b7f 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/search/CustomQueryParser.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/search/CustomQueryParser.java @@ -58,10 +58,9 @@ public CustomQueryParser(String field) { ## CommonQueryParserConfiguration.setLowerCaseExpandedTerms removed (LUCENE-7355) -This option has been removed as expanded terms are now normalized through +Expanded terms are now normalized through Analyzer#normalize. */ - // setLowercaseExpandedTerms(false); } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/search/QueryBuilder.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/search/QueryBuilder.java index cfaa0769697..494d158694e 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/search/QueryBuilder.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/search/QueryBuilder.java @@ -220,9 +220,10 @@ public String getDirPath() { * @param path a defined value * @return a defined, transformed value */ + @SuppressWarnings("java:S4790") public static String normalizeDirPath(String path) { String norm2; - if (path.length() > 0) { + if (!path.isEmpty()) { String norm1 = path.replace(File.separatorChar, '/'); norm2 = norm1.endsWith("/") ? norm1 : norm1 + "/"; } else { diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/search/SettingsHelper.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/search/SettingsHelper.java index 9b923257563..b8b2e845393 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/search/SettingsHelper.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/search/SettingsHelper.java @@ -35,6 +35,7 @@ import java.util.Comparator; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.TreeMap; /** @@ -71,10 +72,9 @@ public Map getSymlinks(String projectName) throws IOExce getSettings(projectName); String projectKey = projectName != null ? projectName : ""; Map indexSymlinks = mappedIndexedSymlinks.get(projectKey); - if (indexSymlinks != null) { - return Collections.unmodifiableMap(indexSymlinks); - } - return null; + return Optional.ofNullable(indexSymlinks) + .map(Collections::unmodifiableMap) + .orElseGet(Collections::emptyMap); } /** diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/search/TermEscaperBase.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/search/TermEscaperBase.java index 662a3dbeb03..47439e08804 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/search/TermEscaperBase.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/search/TermEscaperBase.java @@ -63,10 +63,10 @@ void appendOut(String s) { * that the argument to {@link #setOut(StringBuilder)} contains the entire * transformation. */ + @SuppressWarnings("java:S3626") void consume() { try { while (yylex()) { - //noinspection UnnecessaryContinue continue; } } catch (IOException ex) { diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/search/context/Context.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/search/context/Context.java index 7639322a5d6..d7530547796 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/search/context/Context.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/search/context/Context.java @@ -29,6 +29,7 @@ import java.io.Writer; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; @@ -278,7 +279,9 @@ public boolean getContext(Reader in, Writer out, String urlPrefix, } boolean anything = false; TreeMap matchingTags = null; - String urlPrefixE = (urlPrefix == null) ? "" : Util.uriEncodePath(urlPrefix); + String urlPrefixE = Optional.ofNullable(urlPrefix) + .map(Util::uriEncodePath) + .orElse(""); String pathE = Util.uriEncodePath(path); if (tags != null) { matchingTags = new TreeMap<>(); @@ -298,7 +301,7 @@ public boolean getContext(Reader in, Writer out, String urlPrefix, /* desc[0] is matched symbol * desc[1] is line number * desc[2] is type - * desc[3] is matching line; + * desc[3] is matching line * desc[4] is scope */ String[] desc = { @@ -350,7 +353,7 @@ public boolean getContext(Reader in, Writer out, String urlPrefix, } catch (Exception e) { if (hits != null) { // @todo verify why we ignore all exceptions? - LOGGER.log(Level.WARNING, "Could not get context for " + path, e); + LOGGER.log(Level.WARNING, e, () -> "Could not get context for " + path); } } } @@ -406,19 +409,17 @@ public boolean getContext(Reader in, Writer out, String urlPrefix, tokens.setFilename(path); } - int limit_max_lines = env.getContextLimit(); + int limitMaxLines = env.getContextLimit(); try { String token; int matchState; int matchedLines = 0; while ((token = tokens.yylex()) != null && (!lim || - matchedLines < limit_max_lines)) { + matchedLines < limitMaxLines)) { for (LineMatcher lineMatcher : m) { matchState = lineMatcher.match(token); if (matchState == LineMatcher.MATCHED) { - if (!isDefSearch) { - tokens.printContext(); - } else if (tokens.tags.containsKey(tokens.markedLine)) { + if (!isDefSearch || tokens.tags.containsKey(tokens.markedLine)) { tokens.printContext(); } matchedLines++; @@ -432,7 +433,7 @@ public boolean getContext(Reader in, Writer out, String urlPrefix, } anything = matchedLines > 0; tokens.dumpRest(); - if (lim && (truncated || matchedLines == limit_max_lines) && out != null) { + if (lim && (truncated || matchedLines == limitMaxLines) && out != null) { out.write("[all...]"); } } catch (IOException e) { diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/search/context/HistoryContext.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/search/context/HistoryContext.java index b119152d80e..516c311ea8f 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/search/context/HistoryContext.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/search/context/HistoryContext.java @@ -30,6 +30,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; @@ -175,9 +176,8 @@ private int matchLine(String line, String urlPrefix, String path, @Nullable Writ @VisibleForTesting boolean getHistoryContext(History history, String path, @Nullable Writer out, @Nullable List hits, String urlPrefix) { - if (history == null) { - throw new IllegalArgumentException("`in' is null"); - } + history = Optional.ofNullable(history) + .orElseThrow(() -> new IllegalArgumentException("`in' is null")); if ((out == null) == (hits == null)) { // There should be exactly one destination for the output. If // none or both are specified, it's a bug. @@ -218,7 +218,7 @@ boolean getHistoryContext(History history, String path, @Nullable Writer out, @N matchedLines += matchLine(line, urlPrefix, path, out, hits, rev, nrev); } } catch (Exception e) { - LOGGER.log(Level.WARNING, "Could not get history context for " + path, e); + LOGGER.log(Level.WARNING, e, () -> "Could not get history context for " + path); } return matchedLines > 0; } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/search/context/PhraseMatcher.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/search/context/PhraseMatcher.java index 017eefed86b..e02e6a75a8c 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/search/context/PhraseMatcher.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/search/context/PhraseMatcher.java @@ -38,12 +38,10 @@ class PhraseMatcher extends LineMatcher { @Override public int match(String token) { if (equal(token, phraseTerms[cur])) { - //System.out.println(" PhraseMatcher matched " + token); if (cur < phraseTerms.length - 1) { cur++; return WAIT; //matching. } - //System.out.println(" PhraseMatcher match complete with " + token); cur = 0; return MATCHED; //matched! } else if (cur > 0) { diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/ColorUtil.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/ColorUtil.java index 3e2e2a8a5cb..c68c64d01ae 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/ColorUtil.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/ColorUtil.java @@ -20,7 +20,7 @@ /* * Copyright 2000-2016 JetBrains s.r.o. * - * Licensed under the Apache License, Version 2.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License") * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/CtagsUtil.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/CtagsUtil.java index 735e0dff412..3c87fff0dcb 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/CtagsUtil.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/CtagsUtil.java @@ -40,11 +40,13 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.Objects; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; public class CtagsUtil { @@ -180,11 +182,10 @@ public static void deleteTempFiles() { // hard-coded TMPDIR in Universal Ctags on Unix. dirs.add("/tmp"); } - + dirs = dirs.stream() + .filter(Objects::nonNull) + .collect(Collectors.toSet()); for (String directoryName : dirs) { - if (directoryName == null) { - continue; - } File directory = new File(directoryName); if (!directory.isDirectory()) { diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/Executor.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/Executor.java index b0d23ae2eba..4b8f59eda32 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/Executor.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/Executor.java @@ -35,12 +35,14 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Timer; import java.util.TimerTask; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import org.apache.commons.lang3.SystemUtils; import org.opengrok.indexer.configuration.RuntimeEnvironment; @@ -172,11 +174,9 @@ public int exec(final boolean reportExceptions, StreamHandler handler) { } File cwd = processBuilder.directory(); - if (cwd == null) { - dir_str = System.getProperty("user.dir"); - } else { - dir_str = cwd.toString(); - } + dir_str = Optional.ofNullable(cwd) + .map(File::toString) + .orElseGet(() -> System.getProperty("user.dir")); String envStr = ""; if (LOGGER.isLoggable(Level.FINER)) { @@ -209,7 +209,6 @@ public int exec(final boolean reportExceptions, StreamHandler handler) { }); thread.start(); - int timeout = this.timeout; /* * Setup timer so if the process get stuck we can terminate it and * make progress instead of hanging the whole operation. @@ -268,7 +267,7 @@ public int exec(final boolean reportExceptions, StreamHandler handler) { } if (ret != 0 && reportExceptions) { - int MAX_MSG_SZ = 512; /* limit to avoid flooding the logs */ + int maxMsgSize = 512; /* limit to avoid flooding the logs */ StringBuilder msg = new StringBuilder("Non-zero exit status ") .append(ret).append(" from command [") .append(cmd_str) @@ -277,8 +276,8 @@ public int exec(final boolean reportExceptions, StreamHandler handler) { append("'"); if (stderr != null && stderr.length > 0) { msg.append(": "); - if (stderr.length > MAX_MSG_SZ) { - msg.append(new String(stderr, 0, MAX_MSG_SZ)).append("..."); + if (stderr.length > maxMsgSize) { + msg.append(new String(stderr, 0, maxMsgSize)).append("..."); } else { msg.append(new String(stderr)); } @@ -412,23 +411,15 @@ public static void registerErrorHandler() { * @return a defined instance */ public static String escapeForShell(List argv, boolean multiline, boolean isWindows) { - StringBuilder result = new StringBuilder(); - for (int i = 0; i < argv.size(); ++i) { - if (multiline && i > 0) { - result.append("\t"); - } - String arg = argv.get(i); - result.append(isWindows ? maybeEscapeForPowerShell(arg) : maybeEscapeForSh(arg)); - if (i + 1 < argv.size()) { - if (!multiline) { - result.append(" "); - } else { - result.append(isWindows ? " `" : " \\"); - result.append(System.lineSeparator()); - } - } - } - return result.toString(); + return argv.stream() + .map(arg -> isWindows ? maybeEscapeForPowerShell(arg) : maybeEscapeForSh(arg)) + .collect(Collectors.joining(multiline ? multiLineDelimiter(isWindows) : " ")); + } + + private static String multiLineDelimiter(boolean isWindows) { + return (isWindows ? " `" : " \\") + + System.lineSeparator() + + "\t"; } private static String maybeEscapeForSh(String value) { diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/Getopt.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/Getopt.java index 1cdd417e12b..ee212b02a00 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/Getopt.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/Getopt.java @@ -34,7 +34,7 @@ */ public class Getopt { private static class Option { - char option; + char optOption; String argument; } @@ -80,7 +80,7 @@ public void parse() throws ParseException { } Option option = new Option(); - option.option = chars[jj]; + option.optOption = chars[jj]; options.add(option); // does this option take an argument? if ((idx + 1) < opts.length() && (opts.charAt(idx + 1) == ':')) { @@ -117,7 +117,7 @@ public int getOpt() { ++current; if (current < options.size()) { - ret = options.get(current).option; + ret = options.get(current).optOption; } return ret; diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/HeadHandler.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/HeadHandler.java index a9bc5492e56..21fcd5c0681 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/HeadHandler.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/HeadHandler.java @@ -43,7 +43,7 @@ public class HeadHandler implements Executor.StreamHandler { private final List lines = new ArrayList<>(); private final Charset charset; - private static final int bufferedReaderSize = 200; + private static final int BUFFERED_READER_SIZE = 200; /** * Charset of the underlying reader is set to UTF-8. @@ -80,14 +80,14 @@ public String get(int index) { // for testing static int getBufferedReaderSize() { - return bufferedReaderSize; + return BUFFERED_READER_SIZE; } @Override public void processStream(InputStream input) throws IOException { try (BufferedInputStream bufStream = new BufferedInputStream(input); BufferedReader reader = new BufferedReader(new InputStreamReader(bufStream, this.charset), - bufferedReaderSize)) { + BUFFERED_READER_SIZE)) { int lineNum = 0; while (lineNum < maxLines) { String line = reader.readLine(); diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/HostUtil.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/HostUtil.java index 97200d17018..1e2ff04b609 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/HostUtil.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/HostUtil.java @@ -110,7 +110,7 @@ public static boolean isReachable(String webappURI, int timeOutSeconds, @Nullabl try { int port = HostUtil.urlToPort(webappURI); if (port <= 0) { - LOGGER.log(Level.SEVERE, "invalid port number for " + webappURI); + LOGGER.log(Level.SEVERE, () -> "invalid port number for " + webappURI); return false; } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/LineBreaker.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/LineBreaker.java index 31c0a4ace40..9624bf980d5 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/LineBreaker.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/LineBreaker.java @@ -36,6 +36,8 @@ */ public class LineBreaker { + private static final String RESET_FAILED_MSG = "reset() did not succeed"; + private int length; private int count; private int[] lineOffsets; @@ -113,7 +115,7 @@ public int originalLength() { */ public int count() { if (lineOffsets == null) { - throw new IllegalStateException("reset() did not succeed"); + throw new IllegalStateException(RESET_FAILED_MSG); } return count; } @@ -128,7 +130,7 @@ public int count() { */ public int getOffset(int index) { if (lineOffsets == null) { - throw new IllegalStateException("reset() did not succeed"); + throw new IllegalStateException(RESET_FAILED_MSG); } if (index < 0 || index >= lineOffsets.length) { throw new IllegalArgumentException("index is out of bounds"); @@ -145,7 +147,7 @@ public int getOffset(int index) { */ public int findLineIndex(int offset) { if (lineOffsets == null) { - throw new IllegalStateException("reset() did not succeed"); + throw new IllegalStateException(RESET_FAILED_MSG); } return SplitterUtil.findLineIndex(length, lineOffsets, offset); } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/NullWriter.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/NullWriter.java index 1e614658dcd..93faa0029b6 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/NullWriter.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/NullWriter.java @@ -35,14 +35,17 @@ public class NullWriter extends Writer { @Override public void write(char[] chars, int i, int i1) throws IOException { + // No Operation } @Override public void flush() throws IOException { + // No Operation } @Override public void close() throws IOException { + // No Operation } } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/OptionParser.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/OptionParser.java index 48c3caf8208..95a56767674 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/OptionParser.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/OptionParser.java @@ -199,7 +199,7 @@ String getUsage() { line.append("\n"); if (description != null) { line.append("\t"); - line.append(description.toString().replaceAll("\\n", "\n\t")); + line.append(description.toString().replace("\n", "\n\t")); } return line.toString(); @@ -546,11 +546,9 @@ public String[] parse(String[] args) throws ParseException { option = getOption(args[ii], ii); // When scanning for specific options... - if (scanning) { - if (option == null || (option = candidate(option, ii)) == null) { - optind = ++ii; // skip over everything else - continue; - } + if (scanning && (option == null || (option = candidate(option, ii)) == null)) { + optind = ++ii; // skip over everything else + continue; } if (option == null) { // no more options? we be done. @@ -589,7 +587,7 @@ public String[] parse(String[] args) throws ParseException { // When option is last in list... if (ii >= args.length) { - if (!opt.mandatory) { + if (Boolean.FALSE.equals(opt.mandatory)) { opt.value = ""; // indicate this option's value was optional } } else { @@ -614,13 +612,11 @@ public String[] parse(String[] args) throws ParseException { } // Only specific values allowed? - if (opt.allowedValues != null) { - if (!opt.allowedValues.contains(opt.value)) { - throw new ParseException( - "'" + opt.value + - "' is unknown value for option " + opt.names + - ". Must be one of " + opt.allowedValues, ii); - } + if (opt.allowedValues != null && !opt.allowedValues.contains(opt.value)) { + throw new ParseException( + "'" + opt.value + + "' is unknown value for option " + opt.names + + ". Must be one of " + opt.allowedValues, ii); } Object value = opt.value; diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/PathUtils.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/PathUtils.java index 36d3471cee1..ad1120e1db1 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/PathUtils.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/PathUtils.java @@ -32,6 +32,7 @@ import java.nio.file.Paths; import java.util.Deque; import java.util.LinkedList; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.logging.Level; @@ -135,14 +136,12 @@ public static String getRelativeToCanonical(Path path, Path canonical, } // optional symbolic-link check - if (allowedSymlinks != null) { - if (Files.isSymbolicLink(iterPath) && - !isWhitelisted(iterCanon.toString(), canonicalRoots) && - !isAllowedSymlink(iterCanon, allowedSymlinks)) { + if (Objects.nonNull(allowedSymlinks) && Files.isSymbolicLink(iterPath) && + !isWhitelisted(iterCanon.toString(), canonicalRoots) && + !isAllowedSymlink(iterCanon, allowedSymlinks)) { String format = String.format("%1$s is prohibited symlink", iterPath); LOGGER.finest(format); throw new ForbiddenSymlinkException(format); - } } String rel = null; @@ -153,7 +152,7 @@ public static String getRelativeToCanonical(Path path, Path canonical, } if (rel != null) { if (tail != null) { - while (tail.size() > 0) { + while (!tail.isEmpty()) { rel = Paths.get(rel, tail.pop()).toString(); } } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/Progress.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/Progress.java index d5fb345b6a6..63fe5713d40 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/Progress.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/Progress.java @@ -130,7 +130,7 @@ private void spawnLogThread() { // spawn a logger thread. run = true; loggerThread = new Thread(this::logLoop, - "progress-thread-" + suffix.replaceAll(" ", "_")); + "progress-thread-" + suffix.replace(" ", "_")); loggerThread.start(); } @@ -158,20 +158,20 @@ private void logLoop() { Map lastLoggedChunk = new HashMap<>(); while (true) { - long currentCount = this.currentCount.get(); + long longCurrentCount = this.currentCount.get(); Level currentLevel = Level.FINEST; // Do not log if there was no progress. - if (cachedCount < currentCount) { - currentLevel = getLevel(lastLoggedChunk, currentCount, currentLevel); - logIt(lastLoggedChunk, currentCount, currentLevel); + if (cachedCount < longCurrentCount) { + currentLevel = getLevel(lastLoggedChunk, longCurrentCount, currentLevel); + logIt(lastLoggedChunk, longCurrentCount, currentLevel); } if (!run) { return; } - cachedCount = currentCount; + cachedCount = longCurrentCount; // wait for event try { @@ -195,10 +195,10 @@ Level getLevel(Map lastLoggedChunk, long currentCount, Level curren currentLevel = baseLogLevel; } else { // Set the log level based on the "buckets". - for (Level level : levelCountMap.keySet()) { - if (lastLoggedChunk.getOrDefault(level, -1L) < - currentCount / levelCountMap.get(level)) { - currentLevel = level; + for (var entry : levelCountMap.entrySet()) { + if (lastLoggedChunk.getOrDefault(entry.getKey(), -1L) < + currentCount / entry.getValue()) { + currentLevel = entry.getKey(); break; } } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/RainbowColorGenerator.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/RainbowColorGenerator.java index 921a3a30e37..e703c286526 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/RainbowColorGenerator.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/RainbowColorGenerator.java @@ -20,7 +20,7 @@ /* * Copyright 2000-2016 JetBrains s.r.o. * - * Licensed under the Apache License, Version 2.0 (the "License"); + * Licensed under the Apache License, Version 2.0 (the "License") * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/SourceSplitter.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/SourceSplitter.java index f103c031aef..7942f6c59a2 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/SourceSplitter.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/SourceSplitter.java @@ -36,6 +36,8 @@ */ public class SourceSplitter { + private static final String RESET_FAILED_MSG = "reset() did not succeed"; + private int length; private String[] lines; private int[] lineOffsets; @@ -52,7 +54,7 @@ public int originalLength() { */ public int count() { if (lines == null) { - throw new IllegalStateException("reset() did not succeed"); + throw new IllegalStateException(RESET_FAILED_MSG); } return lines.length; } @@ -66,7 +68,7 @@ public int count() { */ public String getLine(int index) { if (lines == null) { - throw new IllegalStateException("reset() did not succeed"); + throw new IllegalStateException(RESET_FAILED_MSG); } if (index < 0 || index >= lines.length) { throw new IllegalArgumentException("index is out of bounds"); @@ -84,7 +86,7 @@ public String getLine(int index) { */ public int getOffset(int index) { if (lineOffsets == null) { - throw new IllegalStateException("reset() did not succeed"); + throw new IllegalStateException(RESET_FAILED_MSG); } if (index < 0 || index >= lineOffsets.length) { throw new IllegalArgumentException("index is out of bounds"); @@ -101,7 +103,7 @@ public int getOffset(int index) { */ public int findLineIndex(int offset) { if (lineOffsets == null) { - throw new IllegalStateException("reset() did not succeed"); + throw new IllegalStateException(RESET_FAILED_MSG); } return SplitterUtil.findLineIndex(length, lineOffsets, offset); } @@ -122,7 +124,7 @@ public void reset(String original) { * Should not get here, as String and StringReader operations cannot * throw IOException. */ - throw new RuntimeException(ex); + throw new WrapperIOException(ex); } } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/TandemFilename.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/TandemFilename.java index 32399eacbd0..7aedc2c9a09 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/TandemFilename.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/TandemFilename.java @@ -152,6 +152,7 @@ private static String maybePackSha(String filename, String asciiExtension) { return filename.substring(0, newLength) + asciiExtension; } + @SuppressWarnings("java:S112") private static String sha256base64(String value) { MessageDigest hasher; diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/util/WrapperIOException.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/WrapperIOException.java new file mode 100644 index 00000000000..3080b7e401e --- /dev/null +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/util/WrapperIOException.java @@ -0,0 +1,56 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * See LICENSE.txt included in this distribution for the specific + * language governing permissions and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at LICENSE.txt. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2023, Oracle and/or its affiliates. + * Portions Copyright (c) 2023, Gino Augustine . + */ +package org.opengrok.indexer.util; + +import java.io.IOException; + +/** + * Runtime exception thrown instead IO Exception. + * This is useful since java streams api don't support api throwing checked exception + * + * @author Gino Augustine + */ +public class WrapperIOException extends RuntimeException { + private static final long serialVersionUID = 1L; + + /** + * Constructs a new runtime exception with the specified cause and a + * detail message of {@code (cause==null ? null : cause.toString())} + * (which typically contains the class and detail message of + * {@code cause}). This constructor is useful for runtime exceptions + * that are little more than wrappers IO Exceptions. + * + * @param ioException the cause (which is saved for later retrieval by the + * {@link #getCause()} method). (A {@code null} value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + */ + public WrapperIOException(IOException ioException) { + super(ioException); + } + + public IOException getParentIOException() { + return (IOException) getCause(); + } +} diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/web/EftarFileReader.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/web/EftarFileReader.java index da6834065e9..0a79c49fc3b 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/web/EftarFileReader.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/web/EftarFileReader.java @@ -28,9 +28,12 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; +import java.util.Arrays; +import java.util.Objects; import java.util.StringTokenizer; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.stream.Collectors; import org.opengrok.indexer.logger.LoggerFactory; import org.opengrok.indexer.util.IOUtils; @@ -183,17 +186,16 @@ public String getChildTag(FNode fn, String name) throws IOException { * @throws IOException I/O */ public String get(String path) throws IOException { - StringTokenizer toks = new StringTokenizer(path, "/"); f.seek(0); FNode n = new FNode(); FNode next; long tagOffset = 0; int tagLength = 0; - while (toks.hasMoreTokens()) { - String tok = toks.nextToken(); - if (tok == null || tok.length() == 0) { - continue; - } + var tokens = Arrays.stream(path.split("/")) + .filter(Objects::nonNull) + .filter(tok -> !tok.isEmpty()) + .collect(Collectors.toList()); + for (var tok : tokens) { next = n.get(EftarFile.myHash(tok)); if (next == null) { break; diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/web/Laundromat.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/web/Laundromat.java index 3df46bb1f12..edfa7665cb1 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/web/Laundromat.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/web/Laundromat.java @@ -22,9 +22,15 @@ */ package org.opengrok.indexer.web; -import java.util.HashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.Collection; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Represents a container for sanitizing methods for avoiding classifications as @@ -33,7 +39,7 @@ public class Laundromat { private static final String ESC_N_R_T_F = "[\\n\\r\\t\\f]"; - private static final String ESG_N_R_T_F__1_n = ESC_N_R_T_F + "+"; + private static final String ESG_N_R_T_F_1_N = ESC_N_R_T_F + "+"; /** * Sanitize {@code value} where it will be used in subsequent OpenGrok @@ -52,7 +58,7 @@ public static String launderInput(String value) { * replaced with one space. */ public static String launderQuery(String value) { - return replaceAll(value, ESG_N_R_T_F__1_n, " "); + return replaceAll(value, ESG_N_R_T_F_1_N, " "); } /** @@ -65,60 +71,55 @@ public static String launderLog(String value) { if (value == null) { return null; } - return value.replaceAll("\\n", ""). - replaceAll("\\r", ""). - replaceAll("\\t", ""). - replaceAll("\\f", ""); + return value.replace("\n", ""). + replace("\r", ""). + replace("\t", ""). + replace("\f", ""); } /** * Sanitize {@code map} where it will be used in a log message only. - * @return {@code null} if null or else {@code map} with keys and values + * @return {@code map} with keys and values * sanitized with {@link #launderLog(String)}. If the sanitizing causes key * collisions, the colliding keys' values are combined. */ - public static Map launderLog(Map map) { - if (map == null) { - return null; - } + @NotNull + public static Map launderLog(@Nullable Map map) { - HashMap safes = new HashMap<>(); - for (Map.Entry entry : map.entrySet().stream().sorted( - Map.Entry.comparingByKey()).collect(Collectors.toList())) { - String key = launderLog(entry.getKey()); - String[] safeValues = safes.get(key); - String[] fullySafe = mergeLogArrays(entry.getValue(), safeValues); - safes.put(key, fullySafe); - } - return safes; + return Optional.ofNullable(map) + .stream() + .map(Map::entrySet) + .flatMap(Collection::stream) + .sorted(Map.Entry.comparingByKey()) + .collect(Collectors.toMap( + entry -> launderLog(entry.getKey()), + entry -> launderLogArray(entry.getValue()), + Laundromat::mergeLogArrays + )); } - private static String[] mergeLogArrays(String[] values, String[] safeValues) { - if (values == null && safeValues == null) { - return null; - } + @NotNull + private static String[] launderLogArray(@Nullable String[] values) { + return Optional.ofNullable(values) + .stream().flatMap(Arrays::stream) + .map(Laundromat::launderLog) + .toArray(String[]::new); + } - int n = (values != null ? values.length : 0) + - (safeValues != null ? safeValues.length : 0); - String[] result = new String[n]; + @NotNull + private static String[] mergeLogArrays(@NotNull String[] existingSafeEntries, + @NotNull String[] newSafeEntries) { + return Stream.concat(Arrays.stream(newSafeEntries), Arrays.stream(existingSafeEntries)) + .toArray(String[]::new); - int i = 0; - if (values != null) { - for (; i < values.length; ++i) { - result[i] = launderLog(values[i]); - } - } - if (safeValues != null) { - System.arraycopy(safeValues, 0, result, i, safeValues.length); - } - return result; } - private static String replaceAll(String value, String regex, String replacement) { - if (value == null) { - return null; - } - return value.replaceAll(regex, replacement); + @Nullable + private static String replaceAll(@Nullable String value, @NotNull String regex, + @NotNull String replacement) { + return Optional.ofNullable(value) + .map(val -> val.replaceAll(regex, replacement)) + .orElse(null); } /* private to enforce static */ diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/web/Prefix.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/web/Prefix.java index c4ab16b2aa3..13980d373ce 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/web/Prefix.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/web/Prefix.java @@ -65,7 +65,7 @@ public enum Prefix { RAW_P("/raw"), /** Full-blown search from main page or top bar (link prefix). */ SEARCH_P("/search"), - /** Search from cross reference, can lead to direct match (which opens + /** Search from cross-reference, can lead to direct match (which opens * directly) or to a matches Summary page. */ SEARCH_R("/s"), /** opensearch description page. */ @@ -85,9 +85,9 @@ public enum Prefix { /** JavaScript in Web JARs. */ WEBJARS("/webjars"); - private final String prefix; - Prefix(String prefix) { - this.prefix = prefix; + private final String strPrefix; + Prefix(String strPrefix) { + this.strPrefix = strPrefix; } /** @@ -96,7 +96,7 @@ public enum Prefix { */ @Override public String toString() { - return prefix; + return strPrefix; } // should be sufficient for now diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/web/SearchHelper.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/web/SearchHelper.java index b379c4bb0bd..5955265451e 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/web/SearchHelper.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/web/SearchHelper.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; @@ -782,29 +783,26 @@ public String getPrimeRelativePath(String project, String relativePath) throws IOException, ForbiddenSymlinkException { RuntimeEnvironment env = RuntimeEnvironment.getInstance(); - String sourceRoot = env.getSourceRootPath(); - if (sourceRoot == null) { - throw new IllegalStateException("sourceRoot is not defined"); - } - File absolute = new File(sourceRoot + relativePath); + String nonEmptySourceRoot = Optional.ofNullable(env.getSourceRootPath()) + .orElseThrow(() -> new IllegalStateException("sourceRoot is not defined")); + + File absolute = new File(nonEmptySourceRoot + relativePath); ensureSettingsHelper(); settingsHelper.getSettings(project); Map indexedSymlinks = settingsHelper.getSymlinks(project); - if (indexedSymlinks != null) { - String canonical = absolute.getCanonicalFile().getPath(); - for (IndexedSymlink entry : indexedSymlinks.values()) { - if (canonical.equals(entry.getCanonical())) { - if (absolute.getPath().equals(entry.getAbsolute())) { - return relativePath; - } - Path newAbsolute = Paths.get(entry.getAbsolute()); - return env.getPathRelativeToSourceRoot(newAbsolute.toFile()); - } else if (canonical.startsWith(entry.getCanonicalSeparated())) { - Path newAbsolute = Paths.get(entry.getAbsolute(), - canonical.substring(entry.getCanonicalSeparated().length())); - return env.getPathRelativeToSourceRoot(newAbsolute.toFile()); + String canonical = absolute.getCanonicalFile().getPath(); + for (IndexedSymlink entry : indexedSymlinks.values()) { + if (canonical.equals(entry.getCanonical())) { + if (absolute.getPath().equals(entry.getAbsolute())) { + return relativePath; } + Path newAbsolute = Paths.get(entry.getAbsolute()); + return env.getPathRelativeToSourceRoot(newAbsolute.toFile()); + } else if (canonical.startsWith(entry.getCanonicalSeparated())) { + Path newAbsolute = Paths.get(entry.getAbsolute(), + canonical.substring(entry.getCanonicalSeparated().length())); + return env.getPathRelativeToSourceRoot(newAbsolute.toFile()); } } diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/web/Util.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/web/Util.java index 9bedf9f45cd..d0e107fffda 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/web/Util.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/web/Util.java @@ -329,9 +329,7 @@ private static boolean needsHtmlize(char c, boolean pre) { case '<': return true; case '\n': - if (!pre) { - return true; - } + return !pre; default: return (c < ' ' || c > '~') && (c >= ' ' || !Character.isWhitespace(c)); } @@ -1641,6 +1639,7 @@ public static String linkifyPattern(String text, Pattern pattern, String name, S * @param req the request containing the information about the server * @return the converted URL or the input parameter if there was an error */ + @SuppressWarnings("java:S1149") public static String completeUrl(String url, HttpServletRequest req) { try { if (!isHttpUri(url)) { diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/web/api/constraints/PositiveDurationValidator.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/web/api/constraints/PositiveDurationValidator.java index 0647526895d..f4a2e3dcc22 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/web/api/constraints/PositiveDurationValidator.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/web/api/constraints/PositiveDurationValidator.java @@ -34,7 +34,7 @@ public class PositiveDurationValidator implements ConstraintValidator()); - } + tagMessages.computeIfAbsent(tag, key -> new TreeSet<>()); if (tagMessages.get(tag).add(acceptedMessage)) { messagesInTheSystem++; added = true; diff --git a/opengrok-indexer/src/main/java/org/opengrok/indexer/web/messages/MessagesUtils.java b/opengrok-indexer/src/main/java/org/opengrok/indexer/web/messages/MessagesUtils.java index 27cb1b14631..e34531d1918 100644 --- a/opengrok-indexer/src/main/java/org/opengrok/indexer/web/messages/MessagesUtils.java +++ b/opengrok-indexer/src/main/java/org/opengrok/indexer/web/messages/MessagesUtils.java @@ -32,11 +32,11 @@ import java.io.IOException; import java.io.Writer; -import java.sql.Date; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; diff --git a/opengrok-indexer/src/test/java/org/opengrok/indexer/analysis/AnalyzerGuruTest.java b/opengrok-indexer/src/test/java/org/opengrok/indexer/analysis/AnalyzerGuruTest.java index 8d9fab21eb9..d240e055e09 100644 --- a/opengrok-indexer/src/test/java/org/opengrok/indexer/analysis/AnalyzerGuruTest.java +++ b/opengrok-indexer/src/test/java/org/opengrok/indexer/analysis/AnalyzerGuruTest.java @@ -50,12 +50,12 @@ import org.opengrok.indexer.analysis.sh.ShAnalyzerFactory; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; /** * Tests for the functionality provided by the AnalyzerGuru class. @@ -65,7 +65,7 @@ class AnalyzerGuruTest { @Test void testGetFileTypeDescriptions() { Map map = AnalyzerGuru.getfileTypeDescriptions(); - assertTrue(map.size() > 0); + assertFalse(map.isEmpty()); } /** diff --git a/opengrok-indexer/src/test/java/org/opengrok/indexer/analysis/XrefTestBase.java b/opengrok-indexer/src/test/java/org/opengrok/indexer/analysis/XrefTestBase.java index 06b0008af4b..cb8b58cc2d5 100644 --- a/opengrok-indexer/src/test/java/org/opengrok/indexer/analysis/XrefTestBase.java +++ b/opengrok-indexer/src/test/java/org/opengrok/indexer/analysis/XrefTestBase.java @@ -105,7 +105,7 @@ private int writeXref( writeArgs.setDefs(defs); Xrefer xref = analyzer.writeXref(writeArgs); - oss.print(out.toString()); + oss.print(out); oss.print(getHtmlEnd()); return xref.getLOC(); diff --git a/opengrok-indexer/src/test/java/org/opengrok/indexer/analysis/yaml/YamlSymbolTokenizerTest.java b/opengrok-indexer/src/test/java/org/opengrok/indexer/analysis/yaml/YamlSymbolTokenizerTest.java index 9fce4adc471..14ff708f106 100644 --- a/opengrok-indexer/src/test/java/org/opengrok/indexer/analysis/yaml/YamlSymbolTokenizerTest.java +++ b/opengrok-indexer/src/test/java/org/opengrok/indexer/analysis/yaml/YamlSymbolTokenizerTest.java @@ -47,7 +47,7 @@ class YamlSymbolTokenizerTest { private final AbstractAnalyzer analyzer; YamlSymbolTokenizerTest() { - this.analyzer = YamlAnalyzerFactory.DEFAULT_INSTANCE.getAnalyzer(); + this.analyzer = new YamlAnalyzerFactory().getAnalyzer(); } private String[] getTermsFor(Reader r) { diff --git a/opengrok-indexer/src/test/java/org/opengrok/indexer/analysis/yaml/YamlXrefTest.java b/opengrok-indexer/src/test/java/org/opengrok/indexer/analysis/yaml/YamlXrefTest.java index 28b8c0f5514..ec0a3afdd75 100644 --- a/opengrok-indexer/src/test/java/org/opengrok/indexer/analysis/yaml/YamlXrefTest.java +++ b/opengrok-indexer/src/test/java/org/opengrok/indexer/analysis/yaml/YamlXrefTest.java @@ -37,7 +37,7 @@ class YamlXrefTest extends XrefTestBase { @Test @SuppressWarnings("squid:S2699") void sampleTest() throws IOException { - writeAndCompare(YamlAnalyzerFactory.DEFAULT_INSTANCE, + writeAndCompare(new YamlAnalyzerFactory(), "analysis/yaml/sample.yml", "analysis/yaml/sample_xref.html", readTagsFromResource("analysis/yaml/sampletags"), 21); diff --git a/opengrok-indexer/src/test/java/org/opengrok/indexer/web/LaundromatTest.java b/opengrok-indexer/src/test/java/org/opengrok/indexer/web/LaundromatTest.java index 71ad660bddd..57be5b1c286 100644 --- a/opengrok-indexer/src/test/java/org/opengrok/indexer/web/LaundromatTest.java +++ b/opengrok-indexer/src/test/java/org/opengrok/indexer/web/LaundromatTest.java @@ -75,12 +75,12 @@ void launderLogMap() { Map laundry = Laundromat.launderLog(testMap); Map expected = new HashMap<>(); - expected.put("a", null); + expected.put("a", new String[0]); expected.put("b", new String[]{TEST_CONTENT_LOG_LAUNDRY}); expected.put(TEST_CONTENT_LOG_LAUNDRY, new String[]{"c", "d"}); expected.put("ef", new String[]{"g", "h", "c", "d"}); expected.put("i", new String[]{"k", "j"}); - expected.put("l", null); + expected.put("l", new String[0]); assertEquals(hashedValues(expected), hashedValues(laundry), "maps″ should be equal"); } diff --git a/suggester/src/main/java/org/opengrok/suggest/ComplexQueryData.java b/suggester/src/main/java/org/opengrok/suggest/ComplexQueryData.java new file mode 100644 index 00000000000..6a17abab460 --- /dev/null +++ b/suggester/src/main/java/org/opengrok/suggest/ComplexQueryData.java @@ -0,0 +1,37 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * See LICENSE.txt included in this distribution for the specific + * language governing permissions and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at LICENSE.txt. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2023, Oracle and/or its affiliates. + * Portions Copyright (c) 2023, Gino Augustine . + */ +package org.opengrok.suggest; + +import org.opengrok.suggest.query.PhraseScorer; +import org.opengrok.suggest.query.data.IntsHolder; + +/** + * Stores Suggester Query result Data. + * @author Gino Augustine + */ +class ComplexQueryData { + IntsHolder documentIds; + + PhraseScorer scorer; +} diff --git a/suggester/src/main/java/org/opengrok/suggest/SuggestResultCollector.java b/suggester/src/main/java/org/opengrok/suggest/SuggestResultCollector.java new file mode 100644 index 00000000000..aefff246b3c --- /dev/null +++ b/suggester/src/main/java/org/opengrok/suggest/SuggestResultCollector.java @@ -0,0 +1,131 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * See LICENSE.txt included in this distribution for the specific + * language governing permissions and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at LICENSE.txt. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2023, Oracle and/or its affiliates. + * Portions Copyright (c) 2023, Gino Augustine . + */ +package org.opengrok.suggest; + +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.index.StoredFields; +import org.apache.lucene.search.CollectionTerminatedException; +import org.apache.lucene.search.Collector; +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.LeafCollector; +import org.apache.lucene.search.Scorable; +import org.apache.lucene.search.ScoreMode; +import org.opengrok.suggest.query.PhraseScorer; +import org.opengrok.suggest.query.data.BitIntsHolder; + +import java.io.IOException; + +/** + * Collects Suggester query results. + * @author Gino Augustine + */ +class SuggestResultCollector implements Collector { + private final LeafReaderContext leafReaderContext; + private final ComplexQueryData data; + private final BitIntsHolder documentIds; + + SuggestResultCollector(LeafReaderContext leafReaderContext, ComplexQueryData data, + BitIntsHolder documentIds) { + this.leafReaderContext = leafReaderContext; + this.data = data; + this.documentIds = documentIds; + } + + /** + * Create a new {@link LeafCollector collector} to collect the given context. + * + * @param context next atomic reader context + */ + @Override + public LeafCollector getLeafCollector(LeafReaderContext context) throws IOException { + return new SuggesterLeafCollector(context); + } + + /** + * Indicates what features are required from the scorer. + */ + @Override + public ScoreMode scoreMode() { + return ScoreMode.COMPLETE_NO_SCORES; + } + + private final class SuggesterLeafCollector implements LeafCollector { + private final LeafReaderContext context; + private final int docBase; + + private SuggesterLeafCollector(LeafReaderContext context) { + this.context = context; + docBase = this.context.docBase; + } + + /** + * Called before successive calls to {@link #collect(int)}. Implementations that need the score of + * the current document (passed-in to {@link #collect(int)}), should save the passed-in Scorer and + * call scorer.score() when needed. + * + * @param scorer scorer + */ + @Override + public void setScorer(Scorable scorer) throws IOException { + if (leafReaderContext == context) { + if (scorer instanceof PhraseScorer) { + data.scorer = (PhraseScorer) scorer; + } else { + try { + // it is mentioned in the documentation that #getChildren should not be called + // in #setScorer but no better way was found + for (var childScorer : scorer.getChildren()) { + if (childScorer.child instanceof PhraseScorer) { + data.scorer = (PhraseScorer) childScorer.child; + } + } + } catch (Exception e) { + // ignore + } + } + } + } + + /** + * Called once for every document matching a query, with the unbased document number. + * + *

Note: The collection of the current segment can be terminated by throwing a {@link + * CollectionTerminatedException}. In this case, the last docs of the current {@link + * LeafReaderContext} will be skipped and {@link IndexSearcher} will + * swallow the exception and continue collection with the next leaf. + * + *

Note: This is called in an inner search loop. For good search performance, implementations + * of this method should not call {@link StoredFields#document} on every hit. Doing so can slow + * searches by an order of magnitude or more. + * + * @param doc documentId + */ + @Override + public void collect(int doc) throws IOException { + if (leafReaderContext == context) { + documentIds.set(docBase + doc); + } + } + } +} diff --git a/suggester/src/main/java/org/opengrok/suggest/SuggesterProjectData.java b/suggester/src/main/java/org/opengrok/suggest/SuggesterProjectData.java index 9e029847abf..42f15287b54 100644 --- a/suggester/src/main/java/org/opengrok/suggest/SuggesterProjectData.java +++ b/suggester/src/main/java/org/opengrok/suggest/SuggesterProjectData.java @@ -55,6 +55,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -289,7 +290,7 @@ private void createSuggesterDir() throws IOException { } } - @SuppressWarnings("java:S2095") + @SuppressWarnings("{java:S2095,java:S1181}") private void initSearchCountMap() throws IOException { searchCountMaps.values().forEach(PopularityMap::close); searchCountMaps.clear(); @@ -310,33 +311,12 @@ private void initSearchCountMap() throws IOException { File f = getChronicleMapFile(field); - ChronicleMapAdapter m; - try { - m = new ChronicleMapAdapter(field, conf.getAverageKeySize(), conf.getEntries(), f); - } catch (IllegalArgumentException e) { - logger.log(Level.SEVERE, String.format("Could not create ChronicleMap for field %s in directory " + - "'%s' due to invalid key size (%f) or number of entries: (%d):", - field, suggesterDir, conf.getAverageKeySize(), conf.getEntries()), e); - return; - } catch (Throwable t) { - logger.log(Level.SEVERE, - String.format("Could not create ChronicleMap for field %s in directory '%s'" - + " , most popular completion disabled, if you are using " - + "JDK9+ make sure to specify: " - + "--add-exports java.base/jdk.internal.ref=ALL-UNNAMED " - + "--add-exports java.base/sun.nio.ch=ALL-UNNAMED " - + "--add-exports jdk.unsupported/sun.misc=ALL-UNNAMED " - + "--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED " - + "--add-opens jdk.compiler/com.sun.tools.javac=ALL-UNNAMED " - + "--add-opens java.base/java.lang=ALL-UNNAMED " - + "--add-opens java.base/java.lang.reflect=ALL-UNNAMED " - + "--add-opens java.base/java.io=ALL-UNNAMED " - + "--add-opens java.base/java.util=ALL-UNNAMED", field, suggesterDir), t); + var chronicleMapAdapter = createChronicleMapAdapter(field, conf, f); + if (Objects.isNull(chronicleMapAdapter)) { return; } - if (getCommitVersion() != getDataVersion()) { - removeOldTerms(m, lookups.get(field)); + removeOldTerms(chronicleMapAdapter, lookups.get(field)); if (conf.getEntries() < lookups.get(field).getCount()) { int newEntriesCount = (int) lookups.get(field).getCount(); @@ -346,11 +326,41 @@ private void initSearchCountMap() throws IOException { conf.setAverageKeySize(newKeyAvgLength); conf.save(suggesterDir, field); - m.resize(newEntriesCount, newKeyAvgLength); + chronicleMapAdapter.resize(newEntriesCount, newKeyAvgLength); } } - searchCountMaps.put(field, m); + searchCountMaps.put(field, chronicleMapAdapter); + + } + } + + @SuppressWarnings("java:S1181") + private ChronicleMapAdapter createChronicleMapAdapter(final String name, + final ChronicleMapConfiguration conf, + final File file) { + ChronicleMapAdapter mapAdapter = null; + try { + mapAdapter = new ChronicleMapAdapter(name, conf.getAverageKeySize(), conf.getEntries(), file); + } catch (IllegalArgumentException e) { + logger.log(Level.SEVERE, String.format("Could not create ChronicleMap for field %s in directory " + + "'%s' due to invalid key size (%f) or number of entries: (%d):", + name, suggesterDir, conf.getAverageKeySize(), conf.getEntries()), e); + } catch (Throwable t) { + logger.log(Level.SEVERE, + String.format("Could not create ChronicleMap for field %s in directory '%s'" + + " , most popular completion disabled, if you are using " + + "JDK9+ make sure to specify: " + + "--add-exports java.base/jdk.internal.ref=ALL-UNNAMED " + + "--add-exports java.base/sun.nio.ch=ALL-UNNAMED " + + "--add-exports jdk.unsupported/sun.misc=ALL-UNNAMED " + + "--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED " + + "--add-opens jdk.compiler/com.sun.tools.javac=ALL-UNNAMED " + + "--add-opens java.base/java.lang=ALL-UNNAMED " + + "--add-opens java.base/java.lang.reflect=ALL-UNNAMED " + + "--add-opens java.base/java.io=ALL-UNNAMED " + + "--add-opens java.base/java.util=ALL-UNNAMED", name, suggesterDir), t); } + return mapAdapter; } private File getChronicleMapFile(final String field) { @@ -613,7 +623,7 @@ public long weight() { int add = searchCounts.get(last); return SuggesterUtils.computeScore(indexReader, field, last) - + add * SuggesterSearcher.TERM_ALREADY_SEARCHED_MULTIPLIER; + + (long) add * SuggesterSearcher.TERM_ALREADY_SEARCHED_MULTIPLIER; } return DEFAULT_WEIGHT; diff --git a/suggester/src/main/java/org/opengrok/suggest/SuggesterSearcher.java b/suggester/src/main/java/org/opengrok/suggest/SuggesterSearcher.java index 1bd456937e1..f02eefe6a9f 100644 --- a/suggester/src/main/java/org/opengrok/suggest/SuggesterSearcher.java +++ b/suggester/src/main/java/org/opengrok/suggest/SuggesterSearcher.java @@ -31,20 +31,15 @@ import org.apache.lucene.index.TermsEnum; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; -import org.apache.lucene.search.Collector; import org.apache.lucene.search.DocIdSetIterator; import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.LeafCollector; import org.apache.lucene.search.MatchAllDocsQuery; import org.apache.lucene.search.Query; -import org.apache.lucene.search.Scorable; -import org.apache.lucene.search.ScoreMode; import org.apache.lucene.util.BytesRef; import org.opengrok.suggest.popular.PopularityCounter; import org.opengrok.suggest.query.SuggesterRangeQuery; import org.opengrok.suggest.query.data.BitIntsHolder; import org.opengrok.suggest.query.data.IntsHolder; -import org.opengrok.suggest.query.PhraseScorer; import org.opengrok.suggest.query.SuggesterQuery; import org.opengrok.suggest.query.customized.CustomPhraseQuery; @@ -52,6 +47,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -144,23 +141,24 @@ private List suggest( return Collections.emptyList(); } - boolean shouldLeaveOutSameTerms = shouldLeaveOutSameTerms(query, suggesterQuery); Set tokensAlreadyIncluded = null; - if (shouldLeaveOutSameTerms) { + if (shouldLeaveOutSameTerms(query, suggesterQuery)) { tokensAlreadyIncluded = SuggesterUtils.intoTermsExceptPhraseQuery(query).stream() .filter(t -> t.field().equals(suggesterQuery.getField())) .map(Term::bytes) .collect(Collectors.toSet()); } - boolean needsDocumentIds = query != null && !(query instanceof MatchAllDocsQuery); + boolean needsDocumentIds = Optional.ofNullable(query) + .filter(query1 -> !(query1 instanceof MatchAllDocsQuery)) + .isPresent(); ComplexQueryData complexQueryData = null; if (needsDocumentIds) { complexQueryData = getComplexQueryData(query, leafReaderContext); - if (interrupted) { - return Collections.emptyList(); - } + } + if (interrupted) { + return Collections.emptyList(); } Terms terms = leafReaderContext.reader().terms(suggesterQuery.getField()); @@ -179,12 +177,7 @@ private List suggest( interrupted = true; break; } - - if (needPositionsAndFrequencies) { - postingsEnum = termsEnum.postings(postingsEnum, PostingsEnum.POSITIONS | PostingsEnum.FREQS); - } else { - postingsEnum = termsEnum.postings(postingsEnum, PostingsEnum.NONE); - } + postingsEnum = derivePostingsEnum(postingsEnum, termsEnum, needPositionsAndFrequencies); int score = 0; if (!needsDocumentIds) { @@ -195,12 +188,9 @@ private List suggest( score = getDocumentFrequency(complexQueryData.documentIds, leafReaderContext.docBase, postingsEnum); } - if (score > 0 && (!shouldLeaveOutSameTerms || !tokensAlreadyIncluded.contains(term))) { + if (shouldAddScoreForTerm(score, term, tokensAlreadyIncluded)) { score += searchCounts.get(term) * TERM_ALREADY_SEARCHED_MULTIPLIER; - - if (queue.canInsert(score)) { - queue.insertWithOverflow(new LookupResultItem(term.utf8ToString(), project, score)); - } + insertScoreToQueue(queue, score, term, project); } term = termsEnum.next(); @@ -209,6 +199,29 @@ private List suggest( return queue.getResult(); } + private PostingsEnum derivePostingsEnum(PostingsEnum postingsEnum, + TermsEnum termsEnum, + boolean needPositionsAndFrequencies) throws IOException { + if (needPositionsAndFrequencies) { + return termsEnum.postings(postingsEnum, PostingsEnum.POSITIONS | PostingsEnum.FREQS); + } else { + return termsEnum.postings(postingsEnum, PostingsEnum.NONE); + } + } + + private boolean shouldAddScoreForTerm(int score, BytesRef term, + Set tokensAlreadyIncluded) { + return score > 0 && (Objects.isNull(tokensAlreadyIncluded) || !tokensAlreadyIncluded.contains(term)); + + } + private void insertScoreToQueue(LookupPriorityQueue queue, int score, + BytesRef term, String project) { + if (queue.canInsert(score)) { + queue.insertWithOverflow(new LookupResultItem(term.utf8ToString(), project, score)); + } + + } + private boolean shouldLeaveOutSameTerms(final Query query, final SuggesterQuery suggesterQuery) { if (query instanceof CustomPhraseQuery) { return false; @@ -225,49 +238,7 @@ private ComplexQueryData getComplexQueryData(final Query query, final LeafReader BitIntsHolder documentIds = new BitIntsHolder(); try { - search(query, new Collector() { - @Override - public LeafCollector getLeafCollector(final LeafReaderContext context) { - return new LeafCollector() { - - final int docBase = context.docBase; - - @Override - public void setScorer(final Scorable scorer) { - if (leafReaderContext == context) { - if (scorer instanceof PhraseScorer) { - data.scorer = (PhraseScorer) scorer; - } else { - try { - // it is mentioned in the documentation that #getChildren should not be called - // in #setScorer but no better way was found - for (var childScorer : scorer.getChildren()) { - if (childScorer.child instanceof PhraseScorer) { - data.scorer = (PhraseScorer) childScorer.child; - } - } - } catch (Exception e) { - // ignore - } - } - } - } - - @Override - public void collect(int doc) { - if (leafReaderContext == context) { - documentIds.set(docBase + doc); - } - } - }; - } - - @Override - public ScoreMode scoreMode() { - return ScoreMode.COMPLETE_NO_SCORES; - } - - }); + search(query, new SuggestResultCollector(leafReaderContext, data, documentIds)); } catch (IOException e) { if (Thread.currentThread().isInterrupted()) { interrupted = true; @@ -341,12 +312,4 @@ private static int normalizeDocumentFrequency(final int count, final int documen return (int) (((double) count / documents) * SuggesterUtils.NORMALIZED_DOCUMENT_FREQUENCY_MULTIPLIER); } - private static class ComplexQueryData { - - private IntsHolder documentIds; - - private PhraseScorer scorer; - - } - } diff --git a/suggester/src/main/java/org/opengrok/suggest/popular/impl/chronicle/BytesRefDataAccess.java b/suggester/src/main/java/org/opengrok/suggest/popular/impl/chronicle/BytesRefDataAccess.java index f891930c9ce..971c8f6d1bc 100644 --- a/suggester/src/main/java/org/opengrok/suggest/popular/impl/chronicle/BytesRefDataAccess.java +++ b/suggester/src/main/java/org/opengrok/suggest/popular/impl/chronicle/BytesRefDataAccess.java @@ -33,10 +33,13 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Optional; + /** * {@link BytesRef} data serializer for {@link net.openhft.chronicle.map.ChronicleMap}. * Modified from ... */ +@SuppressWarnings({"java:S2065", "java:S2160"}) public class BytesRefDataAccess extends AbstractData implements DataAccess { /** Cache field. */ @@ -75,9 +78,11 @@ public BytesRef get() { @Override public BytesRef getUsing(@Nullable BytesRef using) { - if (using == null) { - using = new BytesRef(new byte[array.length]); - } else if (using.bytes.length < array.length) { + using = Optional.ofNullable(using) + .orElseGet( () -> + new BytesRef(new byte[array.length]) + ); + if (using.bytes.length < array.length) { using.bytes = new byte[array.length]; } System.arraycopy(array, 0, using.bytes, 0, array.length); diff --git a/suggester/src/main/java/org/opengrok/suggest/popular/impl/chronicle/BytesRefSizedReader.java b/suggester/src/main/java/org/opengrok/suggest/popular/impl/chronicle/BytesRefSizedReader.java index 63baad2404d..57f38f03b64 100644 --- a/suggester/src/main/java/org/opengrok/suggest/popular/impl/chronicle/BytesRefSizedReader.java +++ b/suggester/src/main/java/org/opengrok/suggest/popular/impl/chronicle/BytesRefSizedReader.java @@ -33,10 +33,13 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Optional; + /** * {@link BytesRef} data serializer for {@link net.openhft.chronicle.map.ChronicleMap}. * Modified from ... */ +@SuppressWarnings("java:S6548") public class BytesRefSizedReader implements SizedReader, Marshallable, ReadResolvable { public static final BytesRefSizedReader INSTANCE = new BytesRefSizedReader(); @@ -53,9 +56,11 @@ public BytesRef read(Bytes in, long size, @Nullable BytesRef using) { size + " given. Memory corruption?"); } int arrayLength = (int) size; - if (using == null) { - using = new BytesRef(new byte[arrayLength]); - } else if (using.bytes.length < arrayLength) { + using = Optional.ofNullable(using) + .orElseGet( () -> + new BytesRef(new byte[arrayLength]) + ); + if (using.bytes.length < arrayLength) { using.bytes = new byte[arrayLength]; } in.read(using.bytes, 0, arrayLength); diff --git a/suggester/src/main/java/org/opengrok/suggest/query/customized/CustomExactPhraseScorer.java b/suggester/src/main/java/org/opengrok/suggest/query/customized/CustomExactPhraseScorer.java index d8bb422553e..5434edbdec0 100644 --- a/suggester/src/main/java/org/opengrok/suggest/query/customized/CustomExactPhraseScorer.java +++ b/suggester/src/main/java/org/opengrok/suggest/query/customized/CustomExactPhraseScorer.java @@ -44,7 +44,9 @@ final class CustomExactPhraseScorer extends Scorer implements PhraseScorer { // private static class PostingsAndPosition { private final PostingsEnum postings; private final int offset; - private int freq, upTo, pos; + private int freq; + private int upTo; + private int pos; PostingsAndPosition(PostingsEnum postings, int offset) { this.postings = postings; @@ -156,12 +158,7 @@ private static boolean advancePosition(PostingsAndPosition posting, int target) private int phraseFreq() throws IOException { // reset state - final PostingsAndPosition[] postings = this.postings; - for (PostingsAndPosition posting : postings) { - posting.freq = posting.postings.freq(); - posting.pos = posting.postings.nextPosition(); - posting.upTo = 1; - } + resetPostings(); int freq = 0; final PostingsAndPosition lead = postings[0]; @@ -191,9 +188,8 @@ private int phraseFreq() throws IOException { freq += 1; // custom begins – found a match - if (positions == null) { - positions = new BitIntsHolder(); - } + positions = Optional.ofNullable(positions) + .orElseGet(BitIntsHolder::new); positions.set(phrasePos + offset); // custom ends @@ -205,14 +201,25 @@ private int phraseFreq() throws IOException { } // custom begin – if some positions were found then store them - if (positions != null) { - documentToPositionsMap.put(docID(), positions); - } + Optional.ofNullable(positions) + .ifPresent(this::putPositionInDocumentMap); // custom ends return freq; } + private void putPositionInDocumentMap(IntsHolder positions) { + documentToPositionsMap.put(docID(), positions); + } + + private void resetPostings() throws IOException { + for (PostingsAndPosition posting : postings) { + posting.freq = posting.postings.freq(); + posting.pos = posting.postings.nextPosition(); + posting.upTo = 1; + } + } + // custom begins – special interface implementation /** {@inheritDoc} */ @Override @@ -220,5 +227,4 @@ public IntsHolder getPositions(final int docId) { return documentToPositionsMap.get(docId); } // custom ends - } diff --git a/suggester/src/main/java/org/opengrok/suggest/query/customized/CustomSloppyPhraseScorer.java b/suggester/src/main/java/org/opengrok/suggest/query/customized/CustomSloppyPhraseScorer.java index b3a4562c5d7..42fe34854f8 100644 --- a/suggester/src/main/java/org/opengrok/suggest/query/customized/CustomSloppyPhraseScorer.java +++ b/suggester/src/main/java/org/opengrok/suggest/query/customized/CustomSloppyPhraseScorer.java @@ -157,8 +157,8 @@ private float phraseFreq() throws IOException { } // custom begins - for (PhrasePositions phrasePositions : this.pq) { - allPositions.set(phrasePositions.position + phrasePositions.offset); + for (PhrasePositions localPhrasePositions : this.pq) { + allPositions.set(localPhrasePositions.position + localPhrasePositions.offset); } // custom ends @@ -473,7 +473,7 @@ private void sortRptGroups(ArrayList> rgs) { } /** Detect repetition groups. Done once - for first doc */ - private ArrayList> gatherRptGroups(LinkedHashMap rptTerms) throws IOException { + private ArrayList> gatherRptGroups(LinkedHashMap rptTerms) { PhrasePositions[] rpp = repeatingPPs(rptTerms); ArrayList> res = new ArrayList<>(); if (!hasMultiTermRpts) { @@ -604,7 +604,7 @@ private void unionTermGroups(ArrayList bb) { } /** map each term to the single group that contains it */ - private HashMap termGroups(LinkedHashMap tord, ArrayList bb) throws IOException { + private HashMap termGroups(LinkedHashMap tord, ArrayList bb) { HashMap tg = new HashMap<>(); Term[] t = tord.keySet().toArray(new Term[0]); for (int i=0; i