-
Notifications
You must be signed in to change notification settings - Fork 116
refactor: MatcherOperatorRegistry and default impl #1237
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
refactor: MatcherOperatorRegistry and default impl #1237
Conversation
|
|
||
| public Class<?> getAnchorType() { | ||
| return this.type; | ||
| } |
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.
Exposed a public getter for anchorType in AssignableTypeMatcher to make it accessible from DefaultImpl.
| private void checkPriority(int priority) { | ||
| if (priority < 0) { | ||
| throw new IllegalArgumentException("Priority must be greater than or equal to 0"); | ||
| } | ||
| } |
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.
In DefaultImpl, I used PriorityMatcherOperator to preserve the order. If negative conditions should not be removed, should I create an additional implementation for that?
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.
Yes, it would be helpful if you mentioned that the priority could be less than zero.
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.
It would be helpful to add the {@link DefaultMatcherOperatorRegistry} for an example.
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 change was applied, but it does not appear in the git diff.
| property.javaField?.annotations?.toList() ?: listOf() | ||
|
|
||
| override fun getType(): Class<*> = property.javaField!!.type | ||
| override fun getType(): Class<*> = property.javaField?.type ?: Object::class.java |
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.
To avoid NPEs when javaField is missing, I added a fallback to Object::class.java. Since this method returns Class<*> (Java reflection), Object is more explicit than Any even though both map to java.lang.Object at runtime.
If null is a valid return value, consider changing the return type to Class<*>? instead of falling back to Object::class.java.
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.
Could you please provide some examples of situations in which the javaField is missing?
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.
i commented it below
| @@ -0,0 +1,84 @@ | |||
| package com.navercorp.fixturemonkey.api.matcher; | |||
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.
/*
* Fixture Monkey
*
* Copyright (c) 2021-present NAVER Corp.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Please add the license
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 change was applied, but it does not appear in the git diff.
| import com.navercorp.fixturemonkey.api.property.Property; | ||
| import com.navercorp.fixturemonkey.api.type.Types; | ||
|
|
||
| public class DefaultMatcherOperatorRegistry<T> implements MatcherOperatorRegistry<T> { |
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.
There is no need to inherit right now; please add final.
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.
I think the MatcherOperatorRegistry splits two concerns: the registry and the retriever.
The MatcherOperatorRegistry should have the methods addFirst and addLast.
A new interface is needed to get the List.
The implementations of two interface should be DefaultMatcherOperatorRegistry
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.
I agree with your idea of separating concerns into a Retriever and a Registry.
The original DefaultMatcherOperatorRegistry was responsible for both concerns,
so I thought it would be clearer to rename it to Container.
| public void addFirst(MatcherOperator<T> matcherOperator) { | ||
| addInternal(matcherOperator, true); | ||
| } | ||
|
|
||
| public void addLast(MatcherOperator<T> matcherOperator) { | ||
| addInternal(matcherOperator, false); | ||
| } | ||
|
|
||
| private void addInternal(MatcherOperator<T> matcherOperator, boolean prepend) { | ||
| int seq = sequenceIssuer.incrementAndGet(); | ||
| int sequence = prepend ? -seq : seq; | ||
|
|
||
| Matcher matcher = matcherOperator.getMatcher(); | ||
| T operator = matcherOperator.getOperator(); | ||
|
|
||
| PriorityMatcherOperator<T> priorityMatcherOperator = new PriorityMatcherOperator<>( | ||
| matcher, | ||
| operator, | ||
| sequence | ||
| ); | ||
|
|
||
| if (matcher instanceof ExactTypeMatcher) { | ||
| Class<?> type = ((ExactTypeMatcher)matcher).getType(); | ||
| typeAwareIntrospectors.computeIfAbsent(type, k -> new ArrayList<>()).add(priorityMatcherOperator); | ||
| } else if (matcher instanceof AssignableTypeMatcher) { | ||
| Class<?> anchorType = ((AssignableTypeMatcher)matcher).getAnchorType(); | ||
| typeAssignableIntrospectors.computeIfAbsent(anchorType, k -> new ArrayList<>()) | ||
| .add(priorityMatcherOperator); | ||
| } else { | ||
| typeUnknownIntrospectors.add(priorityMatcherOperator); | ||
| } | ||
| } |
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.
Better readiness would be achieved if the methods were placed behind the overridden methods.
| @@ -0,0 +1,11 @@ | |||
| package com.navercorp.fixturemonkey.api.matcher; | |||
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.
/*
* Fixture Monkey
*
* Copyright (c) 2021-present NAVER Corp.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Please add the license
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 change was applied, but it does not appear in the git diff.
| public interface MatcherOperatorRegistry<T> { | ||
| List<MatcherOperator<T>> values(); | ||
|
|
||
| List<MatcherOperator<T>> get(Property property); |
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.
'get' is ambiguous; it would be better to use getListByProperty.
More getXXX methods may be needed in the future.
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 change was applied, but it does not appear in the git diff.
|
|
||
| import com.navercorp.fixturemonkey.api.property.Property; | ||
|
|
||
| public interface MatcherOperatorRegistry<T> { |
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.
Please add @API(since = "1.1.16", status = EXPERIMENTAL)
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 change was applied, but it does not appear in the git diff.
| MatcherOperatorRetriever<PropertyGenerator> propertyGenerators, | ||
| PropertyGenerator defaultPropertyGenerator, | ||
| List<MatcherOperator<ObjectPropertyGenerator>> objectPropertyGenerators, | ||
| MatcherOperatorRetriever<ObjectPropertyGenerator> objectPropertyGenerators, |
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.
👍
| if (propertyType != UnidentifiableType.class) { | ||
| for (Map.Entry<Class<?>, List<PriorityMatcherOperator<T>>> e : typeAssignableIntrospectors.entrySet()) { |
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.
If the type cannot be identified, it is skipped.
| private void checkPriority(int priority) { | ||
| if (priority < 0) { | ||
| throw new IllegalArgumentException("Priority must be greater than or equal to 0"); | ||
| } | ||
| } |
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 change was applied, but it does not appear in the git diff.
| /** | ||
| * If backed only by a getter, {@code javaProperty} or {@code javaField} may be null and cause NPE. | ||
| * To avoid this, returns {@link Types.UnidentifiableType} when the type cannot be identified. | ||
| * | ||
| * @see com.navercorp.fixturemonkey.kotlin.expression.root | ||
| */ | ||
| override fun getType(): Class<*> = property.javaField?.type ?: Types.UnidentifiableType::class.java | ||
|
|
||
| /** | ||
| * If backed only by a getter, {@code javaProperty} or {@code javaField} may be null and cause NPE. | ||
| * To avoid this, returns {@link Types.UnidentifiableType} when the type cannot be identified. | ||
| * | ||
| * @see com.navercorp.fixturemonkey.kotlin.expression.root | ||
| */ | ||
| override fun getAnnotatedType(): AnnotatedType = | ||
| property.javaField!!.annotatedType | ||
| property.javaField?.annotatedType ?: object : AnnotatedType { | ||
| override fun getType(): Type = Types.UnidentifiableType::class.java | ||
|
|
||
| override fun getAnnotations(): Array<Annotation> = emptyArray() | ||
|
|
||
| override fun <T : Annotation?> getAnnotation(annotationClass: Class<T>): T? = null | ||
|
|
||
| override fun getDeclaredAnnotations(): Array<Annotation> = emptyArray() | ||
| } |
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.
When extracting a JavaClass from a KProperty, the type can only be resolved if it is field-backed.
If the property is backed by a getter, the actual Java type cannot be determined, which may lead to a NullPointerException.
Previously, a generic Object type was returned in such cases, but this was ambiguous.
To make the intent explicit and allow the caller to handle branching logic more clearly, the return value was changed to the existing UnidentifiableType.
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.
This triggered an NPE
Summary
Abstract List<MatcherOperator> behind a small wrapper to make the matching layer extensible.
Introduces MatcherOperatorRegistry as the abstraction that manages registration and lookup of MatcherOperators.
(Related issue: N/A)
⸻
(Optional): Description
This PR refactors the internal storage and lookup of matchers:
⸻
How Has This Been Tested?
⸻
Is the Document updated?