From b45a9a70b464688c09bb74a3ea78d939fd090593 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Thu, 23 Jul 2026 17:06:40 +0200 Subject: [PATCH] feat: improve codeowners lookup performance --- .../codeowners/CodeownersImpl.java | 52 +++------ .../trace/civisibility/codeowners/Entry.java | 14 ++- .../civisibility/codeowners/EntryBuilder.java | 35 +++++- .../civisibility/codeowners/EntryIndex.java | 105 ++++++++++++++++++ .../civisibility/codeowners/Section.java | 28 +++++ .../codeowners/matcher/AsteriskMatcher.java | 4 +- .../codeowners/matcher/CharacterMatcher.java | 4 +- .../codeowners/matcher/CompositeMatcher.java | 4 +- .../matcher/DoubleAsteriskMatcher.java | 6 +- .../codeowners/matcher/EndOfLineMatcher.java | 4 +- .../matcher/EndOfSegmentMatcher.java | 4 +- .../codeowners/matcher/Matcher.java | 2 +- .../matcher/QuestionMarkMatcher.java | 4 +- .../codeowners/matcher/RangeMatcher.java | 6 +- .../codeowners/CodeownersTest.java | 31 ++++++ .../codeowners/EntryBuilderTest.java | 4 +- 16 files changed, 244 insertions(+), 63 deletions(-) create mode 100644 dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/EntryIndex.java create mode 100644 dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/Section.java diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/CodeownersImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/CodeownersImpl.java index 7035c90a128..e0513b124e6 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/CodeownersImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/CodeownersImpl.java @@ -4,11 +4,9 @@ import java.io.BufferedReader; import java.io.IOException; import java.io.Reader; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Deque; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -20,9 +18,9 @@ public class CodeownersImpl implements Codeowners { - private final Collection
sections; + private final List
sections; - private CodeownersImpl(Collection
sections) { + private CodeownersImpl(List
sections) { this.sections = sections; } @@ -32,17 +30,25 @@ private CodeownersImpl(Collection
sections) { */ @Override public @Nullable Collection getOwners(@Nonnull String path) { - char[] pathCharacters = path.toCharArray(); + if (sections.size() == 1) { + Section section = sections.get(0); + if (section.isExcluded(path)) { + return new ArrayList<>(); + } + Entry entry = section.findMatchingEntry(path); + return entry != null ? new ArrayList<>(entry.getOwners()) : null; + } + Set owners = null; for (Section section : sections) { - if (section.isExcluded(pathCharacters)) { + if (section.isExcluded(path)) { if (owners == null) { owners = new LinkedHashSet<>(); } continue; } - Entry entry = section.findMatchingEntry(pathCharacters); + Entry entry = section.findMatchingEntry(path); if (entry != null) { if (owners == null) { owners = new LinkedHashSet<>(); @@ -90,36 +96,4 @@ public static Codeowners parse(Reader r) throws IOException { sections.addAll(namedSections.values()); return new CodeownersImpl(sections); } - - private static final class Section { - - private final Deque entries = new ArrayDeque<>(); - private final Collection exclusions = new ArrayList<>(); - - private void add(Entry entry) { - if (entry.isExclusion()) { - exclusions.add(entry); - } else { - entries.offerFirst(entry); - } - } - - private boolean isExcluded(char[] path) { - for (Entry exclusion : exclusions) { - if (exclusion.getMatcher().consume(path, 0) >= 0) { - return true; - } - } - return false; - } - - private @Nullable Entry findMatchingEntry(char[] path) { - for (Entry entry : entries) { - if (entry.getMatcher().consume(path, 0) >= 0) { - return entry; - } - } - return null; - } - } } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/Entry.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/Entry.java index e35f13c93b6..0d8167d60db 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/Entry.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/Entry.java @@ -1,18 +1,24 @@ package datadog.trace.civisibility.codeowners; import datadog.trace.civisibility.codeowners.matcher.Matcher; +import java.util.ArrayList; import java.util.Collection; +import java.util.LinkedHashSet; +import javax.annotation.Nullable; public class Entry { private final Matcher matcher; private final Collection owners; private final boolean exclusion; + private final @Nullable String indexKey; - public Entry(Matcher matcher, Collection owners, boolean exclusion) { + public Entry( + Matcher matcher, Collection owners, boolean exclusion, @Nullable String indexKey) { this.matcher = matcher; - this.owners = owners; + this.owners = owners.size() > 1 ? new ArrayList<>(new LinkedHashSet<>(owners)) : owners; this.exclusion = exclusion; + this.indexKey = indexKey; } public Matcher getMatcher() { @@ -26,4 +32,8 @@ public Collection getOwners() { public boolean isExclusion() { return exclusion; } + + public @Nullable String getIndexKey() { + return indexKey; + } } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/EntryBuilder.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/EntryBuilder.java index 3f2d88c3e27..9eeab5e1d71 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/EntryBuilder.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/EntryBuilder.java @@ -73,12 +73,13 @@ public EntryBuilder(CharacterMatcher.Factory characterMatcherFactory, String s) offset++; } + String indexKey = parseIndexKey(); Matcher matcher = parseMatcher(); Collection owners = exclusion ? Collections.emptyList() : parseOwners(); if (!exclusion && owners.isEmpty()) { owners = sectionDefaultOwners; } - return new Entry(matcher, owners, exclusion); + return new Entry(matcher, owners, exclusion, indexKey); } catch (Exception e) { log.warn("Skipping malformed CODEOWNERS entry: {}", new String(c), e); @@ -224,6 +225,38 @@ private Matcher parseMatcher() { return new CompositeMatcher(characterMatchers.toArray(new Matcher[0])); } + private @Nullable String parseIndexKey() { + // Index by the first two fixed path segments; patterns without a safe prefix stay linear. + int position = offset; + boolean patternContainsSlashes = c[position] == '/'; + if (patternContainsSlashes) { + position++; + } + int prefixStart = position; + int firstSeparator = -1; + for (; position < c.length && !isPatternTerminator(c[position]); position++) { + char character = c[position]; + if (character == '*' || character == '?' || character == '[') { + return null; + } + if (character == '\\') { + return null; + } + if (character == '/') { + patternContainsSlashes = true; + if (firstSeparator >= 0) { + return new String(c, prefixStart, position - prefixStart); + } + firstSeparator = position; + } + } + + if (!patternContainsSlashes || firstSeparator < 0 || firstSeparator == position - 1) { + return null; + } + return new String(c, prefixStart, position - prefixStart); + } + private boolean consumeDoubleAsterisk() { int position = offset; diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/EntryIndex.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/EntryIndex.java new file mode 100644 index 00000000000..c7fabdc1dce --- /dev/null +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/EntryIndex.java @@ -0,0 +1,105 @@ +package datadog.trace.civisibility.codeowners; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Nullable; + +/** + * Resolves entries by rule priority, switching large rule sets from linear scans to fixed-prefix + * buckets while retaining a fallback for patterns that cannot be indexed. + */ +final class EntryIndex { + + private static final int MIN_INDEX_SIZE = 512; + + private List entries = new ArrayList<>(); + private Map> entriesByKey; + private List unindexedEntries; + private int indexableEntryCount; + + void add(Entry entry, int order) { + IndexedEntry indexedEntry = new IndexedEntry(entry, order); + if (entriesByKey != null) { + index(indexedEntry); + } else { + entries.add(indexedEntry); + if (entry.getIndexKey() != null) { + indexableEntryCount++; + } + if (entries.size() >= MIN_INDEX_SIZE && indexableEntryCount * 2 >= entries.size()) { + entriesByKey = new HashMap<>(); + unindexedEntries = new ArrayList<>(); + for (IndexedEntry existingEntry : entries) { + index(existingEntry); + } + entries = Collections.emptyList(); + } + } + } + + @Nullable + Entry find(String path) { + IndexedEntry entry = findIndexedEntry(path); + return entry != null ? entry.entry : null; + } + + private @Nullable IndexedEntry findIndexedEntry(String path) { + if (entriesByKey == null) { + return findFirstMatch(entries, path); + } + IndexedEntry unindexedMatch = findFirstMatch(unindexedEntries, path); + + int firstSeparator = path.indexOf('/'); + if (firstSeparator < 0) { + return unindexedMatch; + } + int secondSeparator = path.indexOf('/', firstSeparator + 1); + int keyEnd = secondSeparator >= 0 ? secondSeparator : path.length(); + List indexedEntries = entriesByKey.get(path.substring(0, keyEnd)); + IndexedEntry indexedMatch = findFirstMatch(indexedEntries, path); + + if (unindexedMatch == null) { + return indexedMatch; + } + if (indexedMatch == null) { + return unindexedMatch; + } + return indexedMatch.order > unindexedMatch.order ? indexedMatch : unindexedMatch; + } + + private void index(IndexedEntry indexedEntry) { + String indexKey = indexedEntry.entry.getIndexKey(); + if (indexKey != null) { + entriesByKey.computeIfAbsent(indexKey, key -> new ArrayList<>()).add(indexedEntry); + } else { + unindexedEntries.add(indexedEntry); + } + } + + private static @Nullable IndexedEntry findFirstMatch( + @Nullable List entries, String path) { + if (entries != null) { + for (int i = entries.size() - 1; i >= 0; i--) { + IndexedEntry entry = entries.get(i); + if (entry.entry.getMatcher().consume(path, 0) >= 0) { + return entry; + } + } + } + return null; + } + + private static final class IndexedEntry { + + private final Entry entry; + private final int order; + + private IndexedEntry(Entry entry, int order) { + this.entry = entry; + this.order = order; + } + } +} diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/Section.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/Section.java new file mode 100644 index 00000000000..21629317c05 --- /dev/null +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/Section.java @@ -0,0 +1,28 @@ +package datadog.trace.civisibility.codeowners; + +import javax.annotation.Nullable; + +/** Groups ownership and exclusion rules for a CODEOWNERS section while preserving rule order. */ +final class Section { + + private final EntryIndex entries = new EntryIndex(); + private final EntryIndex exclusions = new EntryIndex(); + private int entryOrder; + + void add(Entry entry) { + if (entry.isExclusion()) { + exclusions.add(entry, entryOrder++); + } else { + entries.add(entry, entryOrder++); + } + } + + boolean isExcluded(String path) { + return exclusions.find(path) != null; + } + + @Nullable + Entry findMatchingEntry(String path) { + return entries.find(path); + } +} diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/AsteriskMatcher.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/AsteriskMatcher.java index ca6733afc7e..56fc1a0d782 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/AsteriskMatcher.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/AsteriskMatcher.java @@ -7,8 +7,8 @@ public class AsteriskMatcher implements Matcher { private AsteriskMatcher() {} @Override - public int consume(char[] line, int offset) { - return offset < line.length && line[offset] != '/' ? 1 : -1; + public int consume(String line, int offset) { + return offset < line.length() && line.charAt(offset) != '/' ? 1 : -1; } @Override diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/CharacterMatcher.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/CharacterMatcher.java index 22c7b52b5b2..60116701956 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/CharacterMatcher.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/CharacterMatcher.java @@ -12,8 +12,8 @@ private CharacterMatcher(char character) { } @Override - public int consume(char[] line, int offset) { - return offset < line.length && line[offset] == character ? 1 : -1; + public int consume(String line, int offset) { + return offset < line.length() && line.charAt(offset) == character ? 1 : -1; } @Override diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/CompositeMatcher.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/CompositeMatcher.java index 3c53fc83be1..229ffc9b424 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/CompositeMatcher.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/CompositeMatcher.java @@ -9,11 +9,11 @@ public CompositeMatcher(Matcher[] delegates) { } @Override - public int consume(char[] line, int offset) { + public int consume(String line, int offset) { return consume(line, offset, 0); } - private int consume(char[] line, int offset, int matcherOffset) { + private int consume(String line, int offset, int matcherOffset) { int position = offset; while (matcherOffset < delegates.length) { Matcher delegate = delegates[matcherOffset]; diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/DoubleAsteriskMatcher.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/DoubleAsteriskMatcher.java index f16eddeba85..dbbdada0eed 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/DoubleAsteriskMatcher.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/DoubleAsteriskMatcher.java @@ -7,13 +7,13 @@ public class DoubleAsteriskMatcher implements Matcher { private DoubleAsteriskMatcher() {} @Override - public int consume(char[] line, int offset) { - if (offset == line.length) { + public int consume(String line, int offset) { + if (offset == line.length()) { return -1; } int position = offset; - while (position < line.length && line[position++] != '/') {} + while (position < line.length() && line.charAt(position++) != '/') {} return position - offset; } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/EndOfLineMatcher.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/EndOfLineMatcher.java index 6f9e5119168..9d57b34eaeb 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/EndOfLineMatcher.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/EndOfLineMatcher.java @@ -5,8 +5,8 @@ public class EndOfLineMatcher implements Matcher { public static final Matcher INSTANCE = new EndOfLineMatcher(); @Override - public int consume(char[] line, int offset) { - return offset == line.length ? 0 : -1; + public int consume(String line, int offset) { + return offset == line.length() ? 0 : -1; } @Override diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/EndOfSegmentMatcher.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/EndOfSegmentMatcher.java index d50f9c42d9b..faf8ae05315 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/EndOfSegmentMatcher.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/EndOfSegmentMatcher.java @@ -5,8 +5,8 @@ public class EndOfSegmentMatcher implements Matcher { public static final Matcher INSTANCE = new EndOfSegmentMatcher(); @Override - public int consume(char[] line, int offset) { - return offset == line.length || line[offset] == '/' ? 0 : -1; + public int consume(String line, int offset) { + return offset == line.length() || line.charAt(offset) == '/' ? 0 : -1; } @Override diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/Matcher.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/Matcher.java index 91c5a904a59..abc1293d024 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/Matcher.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/Matcher.java @@ -6,7 +6,7 @@ public interface Matcher { * @return the number of characters matched from the line starting with the offset. Negative value * means matching failed */ - int consume(char[] line, int offset); + int consume(String line, int offset); /** * @return {@code true} if this matcher can be used [0..*] times. {@code false} if the matcher diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/QuestionMarkMatcher.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/QuestionMarkMatcher.java index a53410b6e06..53412624ebd 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/QuestionMarkMatcher.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/QuestionMarkMatcher.java @@ -7,8 +7,8 @@ public class QuestionMarkMatcher implements Matcher { private QuestionMarkMatcher() {} @Override - public int consume(char[] line, int offset) { - return offset < line.length && line[offset] != '/' ? 1 : -1; + public int consume(String line, int offset) { + return offset < line.length() && line.charAt(offset) != '/' ? 1 : -1; } @Override diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/RangeMatcher.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/RangeMatcher.java index c0c61ebfbf2..a2e44bd4f19 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/RangeMatcher.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/codeowners/matcher/RangeMatcher.java @@ -26,10 +26,10 @@ public RangeMatcher(Range... ranges) { } @Override - public int consume(char[] line, int offset) { - if (offset < line.length) { + public int consume(String line, int offset) { + if (offset < line.length()) { for (Range range : ranges) { - if (range.matches(line[offset])) { + if (range.matches(line.charAt(offset))) { return 1; } } diff --git a/dd-java-agent/agent-ci-visibility/src/test/java/datadog/trace/civisibility/codeowners/CodeownersTest.java b/dd-java-agent/agent-ci-visibility/src/test/java/datadog/trace/civisibility/codeowners/CodeownersTest.java index 72d59b996a4..9e9107acf5c 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/java/datadog/trace/civisibility/codeowners/CodeownersTest.java +++ b/dd-java-agent/agent-ci-visibility/src/test/java/datadog/trace/civisibility/codeowners/CodeownersTest.java @@ -11,8 +11,11 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; +import java.io.StringReader; +import java.util.Collection; import java.util.List; import java.util.stream.Stream; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -30,6 +33,34 @@ void testCodeownersMatching(String resource, String path, List expectedO assertEquals(expectedOwners, codeowners.getOwners(path)); } + @Test + void testIndexedAndFallbackEntriesPreservePriority() throws IOException { + StringBuilder content = new StringBuilder(); + for (int i = 0; i < 510; i++) { + content.append("/dummy").append(i).append("/file @dummy\n"); + } + content.append("/service/target @indexed-before\n"); + content.append("/**/target @fallback-after\n"); + content.append("/other/* @fallback-before\n"); + content.append("/other/target @indexed-after\n"); + + Codeowners codeowners = CodeownersImpl.parse(new StringReader(content.toString())); + + assertEquals(singletonList("@fallback-after"), codeowners.getOwners("service/target")); + assertEquals(singletonList("@indexed-after"), codeowners.getOwners("other/target")); + } + + @Test + void testSingleSectionOwnersAreNormalizedAndIndependent() throws IOException { + Codeowners codeowners = CodeownersImpl.parse(new StringReader("* @team @team")); + + Collection owners = codeowners.getOwners("source/File.java"); + assertEquals(singletonList("@team"), owners); + owners.add("@other-team"); + + assertEquals(singletonList("@team"), codeowners.getOwners("source/File.java")); + } + static Stream testCodeownersMatchingArguments() { List globalOwners = asList("@global-owner1", "@global-owner2"); return Stream.of( diff --git a/dd-java-agent/agent-ci-visibility/src/test/java/datadog/trace/civisibility/codeowners/EntryBuilderTest.java b/dd-java-agent/agent-ci-visibility/src/test/java/datadog/trace/civisibility/codeowners/EntryBuilderTest.java index a6b915c612f..8b44a2d5f01 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/java/datadog/trace/civisibility/codeowners/EntryBuilderTest.java +++ b/dd-java-agent/agent-ci-visibility/src/test/java/datadog/trace/civisibility/codeowners/EntryBuilderTest.java @@ -28,8 +28,8 @@ void testEntryMatch(String pattern, List owners, String path, boolean ex Entry exclusionEntry = new EntryBuilder(matcherFactory, "!" + pattern + " " + ownersToString(owners)).parse(); - boolean result = entry.getMatcher().consume(path.toCharArray(), 0) >= 0; - boolean exclusionResult = exclusionEntry.getMatcher().consume(path.toCharArray(), 0) >= 0; + boolean result = entry.getMatcher().consume(path, 0) >= 0; + boolean exclusionResult = exclusionEntry.getMatcher().consume(path, 0) >= 0; assertEquals(owners, entry.getOwners()); assertFalse(entry.isExclusion());