-
Notifications
You must be signed in to change notification settings - Fork 3.8k
[#noissue] Refactor parsing logic to use KeyValueTokenizer
#13315
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| map.put(kv[0], StringUtils.EMPTY_STRING); | ||
| } | ||
| KeyValueTokenizer.KeyValue keyValue = KeyValueTokenizer.tokenize(v, "="); | ||
| map.put(keyValue.getKey(), keyValue.getValue()); |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential null pointer exception if KeyValueTokenizer.tokenize() returns null when the delimiter is not found. The code should check if keyValue is null before calling getKey() and getValue(), or handle the case where the query parameter doesn't contain '='.
| map.put(keyValue.getKey(), keyValue.getValue()); | |
| if (keyValue == null) { | |
| // Handle parameters without '=' by using the whole segment as key and an empty value | |
| map.put(v, ""); | |
| } else { | |
| map.put(keyValue.getKey(), keyValue.getValue()); | |
| } |
| if (words.length != 2) { | ||
| KeyValueTokenizer.KeyValue keyValue = KeyValueTokenizer.tokenize(key, ":"); | ||
| if (keyValue == null) { | ||
| throw new IllegalArgumentException("the key must contain period"); |
Copilot
AI
Jan 20, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The error message says 'the key must contain period' but the delimiter used is ':', not a period. The message should be corrected to match the actual delimiter.
| throw new IllegalArgumentException("the key must contain period"); | |
| throw new IllegalArgumentException("the key must contain ':'"); |
44fcb19 to
7cfb618
Compare
KeyValueTokenizer.KeyValueTokenizer
7cfb618 to
3bf69d7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| class KeyValueTokenizerTest { | ||
|
|
||
| @Test | ||
| void tokenize() { | ||
| KeyValueTokenizer.KeyValue keyValue = KeyValueTokenizer.tokenize("key=value", "="); | ||
| assertEquals("key", keyValue.getKey()); | ||
| assertEquals("value", keyValue.getValue()); | ||
| } | ||
|
|
||
| @Test | ||
| void tokenize2() { | ||
| KeyValueTokenizer.KeyValue keyValue = KeyValueTokenizer.tokenize("key==value", "=="); | ||
| assertEquals("key", keyValue.getKey()); | ||
| assertEquals("value", keyValue.getValue()); | ||
| } | ||
|
|
||
| @Test | ||
| void tokenize_emptyValue() { | ||
| KeyValueTokenizer.KeyValue keyValue = KeyValueTokenizer.tokenize("key=", "="); | ||
| assertEquals("key", keyValue.getKey()); | ||
| assertEquals("", keyValue.getValue()); | ||
| } | ||
|
|
||
| @Test | ||
| void tokenize_emptyKeyValue() { | ||
| KeyValueTokenizer.KeyValue keyValue = KeyValueTokenizer.tokenize("=", "="); | ||
| assertEquals("", keyValue.getKey()); | ||
| assertEquals("", keyValue.getValue()); | ||
| } | ||
|
|
||
| @Test | ||
| void tokenize_empty() { | ||
| KeyValueTokenizer.KeyValue keyValue = KeyValueTokenizer.tokenize("", "="); | ||
| Assertions.assertNull(keyValue); | ||
| } | ||
| } No newline at end of file |
Copilot
AI
Jan 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The KeyValueTokenizerTest is missing a test case for when a null text parameter is passed to the tokenize method. According to line 32 of KeyValueTokenizer.java, the method will throw a NullPointerException if text is null. A test should be added to document this expected behavior, either verifying that a NullPointerException is thrown or that the method handles null gracefully.
| @Test | ||
| public void parseTagTest_emptyValue() { | ||
| Tag tag = TagUtils.parseTag("A:"); | ||
|
|
||
| Assertions.assertEquals("A", tag.getName()); | ||
| Assertions.assertEquals("", tag.getValue()); | ||
| } |
Copilot
AI
Jan 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test suite is missing a test case for when parseTag is called with a string that doesn't contain a colon delimiter. This is particularly important since the refactoring changed the behavior from the old implementation (which used StringUtils.split and handled missing delimiters by treating the entire string as the key with an empty value) to KeyValueTokenizer.tokenize (which returns null when the delimiter is not found). A test should verify the expected behavior in this case.
| * @param text the text to tokenize | ||
| * @param delimiter the delimiter separating key and value | ||
| * @param factory the factory used to create the token | ||
| * @param <T> the type of token to return | ||
| * @return the parsed token, or {@code null} if the delimiter is not found in the text |
Copilot
AI
Jan 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The javadoc comment on line 29 states that the method returns "null if the delimiter is not found in the text", but this important behavior is not consistently documented or handled by all callers. Consider adding an @throws NullPointerException annotation for the text parameter, and potentially adding a note in the javadoc that callers should check for null return values.
| * @param text the text to tokenize | |
| * @param delimiter the delimiter separating key and value | |
| * @param factory the factory used to create the token | |
| * @param <T> the type of token to return | |
| * @return the parsed token, or {@code null} if the delimiter is not found in the text | |
| * @param text the text to tokenize; must not be {@code null} | |
| * @param delimiter the delimiter separating key and value | |
| * @param factory the factory used to create the token | |
| * @param <T> the type of token to return | |
| * @return the parsed token, or {@code null} if the delimiter is not found in the text; callers should | |
| * check for a {@code null} return value | |
| * @throws NullPointerException if {@code text} is {@code null} |
This pull request introduces a new utility class,
KeyValueTokenizer, to standardize and simplify the parsing of key-value pairs across the codebase. The change replaces various manual string splitting logic with this centralized utility, improving code readability and reducing duplication. The update also includes comprehensive unit tests for the new utility and refactors several modules to use it.Key changes include:
1. Introduction of
KeyValueTokenizerutility:KeyValueTokenizerincommons/src/main/java/com/navercorp/pinpoint/common/util/KeyValueTokenizer.java, providing methods for safe, null-aware key-value parsing with customizable factories for different value types.2. Refactoring to use
KeyValueTokenizer:KeyValueTokenizerin:DamengJdbcUrlParser.java). [1] [2] [3]ChannelzSocketLookup.java). [1] [2]ExceptionTraceQueryParameter.java). [1] [2]TagUtils.java). [1] [2] [3]RedisKVPubChannelProvider.java,RedisKVSubChannelProvider.java). [1] [2] [3] [4]3. Testing improvements:
KeyValueTokenizerTest, with thorough unit tests covering various edge cases for the new utility.TagUtilsTest.java).4. Minor related improvements:
TagUtils(e.g., improved JSON tag string cleaning).ServerMapHistogramControlleronly split into two parts for node and link key parsing. [1] [2]These changes collectively improve code maintainability, reduce the risk of subtle bugs in key-value parsing, and make the parsing logic more consistent across the project.