1 | package com.github.dakusui.pcond.core.refl; | |
2 | ||
3 | import com.github.dakusui.pcond.internals.InternalChecks; | |
4 | import com.github.dakusui.pcond.internals.InternalUtils; | |
5 | ||
6 | import java.lang.reflect.Method; | |
7 | import java.util.*; | |
8 | import java.util.stream.IntStream; | |
9 | ||
10 | import static com.github.dakusui.pcond.core.refl.MethodSelector.Utils.isAssignableWithBoxingFrom; | |
11 | import static com.github.dakusui.pcond.internals.InternalChecks.requireArgument; | |
12 | import static java.lang.String.format; | |
13 | import static java.util.Objects.requireNonNull; | |
14 | import static java.util.stream.Collectors.toList; | |
15 | ||
16 | /** | |
17 | * //@formater:off | |
18 | * An interface representing an object that selects {@link Method}s from given ones. | |
19 | * | |
20 | * This interface is used to choose methods that are appropriate to invoke with | |
21 | * given arguments. | |
22 | * //@formater:on | |
23 | */ | |
24 | public interface MethodSelector extends Formattable { | |
25 | /** | |
26 | * Selects methods that can be invoked with given {@code args}. | |
27 | * | |
28 | * @param methods Methods from which returned methods are selected. | |
29 | * @param args Arguments to be passed to selected methods. | |
30 | * @return Selected methods. | |
31 | */ | |
32 | List<Method> select(List<Method> methods, Object[] args); | |
33 | ||
34 | /** | |
35 | * Returns a string that describes this object. | |
36 | * | |
37 | * @return A description of this object | |
38 | */ | |
39 | String describe(); | |
40 | ||
41 | /** | |
42 | * Returns a composed {@link MethodSelector} that first applies this and | |
43 | * then applies {@code another}. | |
44 | * | |
45 | * @param another The method selector to apply after this. | |
46 | * @return The composed method selector | |
47 | */ | |
48 | default MethodSelector andThen(MethodSelector another) { | |
49 |
1
1. andThen : replaced return value with null for com/github/dakusui/pcond/core/refl/MethodSelector::andThen → KILLED |
return new MethodSelector() { |
50 | @Override | |
51 | public List<Method> select(List<Method> methods, Object[] args) { | |
52 |
1
1. select : replaced return value with Collections.emptyList for com/github/dakusui/pcond/core/refl/MethodSelector$1::select → KILLED |
return another.select(MethodSelector.this.select(methods, args), args); |
53 | } | |
54 | ||
55 | @Override | |
56 | public String describe() { | |
57 |
1
1. describe : replaced return value with "" for com/github/dakusui/pcond/core/refl/MethodSelector$1::describe → KILLED |
return format("%s&&%s", MethodSelector.this.describe(), another.describe()); |
58 | } | |
59 | }; | |
60 | } | |
61 | ||
62 | /** | |
63 | * Formats this object using the {@link MethodSelector#describe()} method. | |
64 | */ | |
65 | @Override | |
66 | default void formatTo(Formatter formatter, int flags, int width, int precision) { | |
67 | formatter.format("%s", this.describe()); | |
68 | } | |
69 | ||
70 | class Default implements MethodSelector { | |
71 | @Override | |
72 | public List<Method> select(List<Method> methods, Object[] args) { | |
73 |
1
1. select : replaced return value with Collections.emptyList for com/github/dakusui/pcond/core/refl/MethodSelector$Default::select → KILLED |
return methods |
74 | .stream() | |
75 |
2
1. lambda$select$0 : replaced boolean return with false for com/github/dakusui/pcond/core/refl/MethodSelector$Default::lambda$select$0 → KILLED 2. lambda$select$0 : replaced boolean return with true for com/github/dakusui/pcond/core/refl/MethodSelector$Default::lambda$select$0 → KILLED |
.filter(m -> areArgsCompatible(m.getParameterTypes(), args)) |
76 | .collect(toList()); | |
77 | } | |
78 | ||
79 | @Override | |
80 | public String describe() { | |
81 |
1
1. describe : replaced return value with "" for com/github/dakusui/pcond/core/refl/MethodSelector$Default::describe → KILLED |
return "default"; |
82 | } | |
83 | ||
84 | private static boolean areArgsCompatible(Class<?>[] formalParameters, Object[] args) { | |
85 |
1
1. areArgsCompatible : negated conditional → KILLED |
if (formalParameters.length != args.length) |
86 |
1
1. areArgsCompatible : replaced boolean return with true for com/github/dakusui/pcond/core/refl/MethodSelector$Default::areArgsCompatible → KILLED |
return false; |
87 |
2
1. areArgsCompatible : changed conditional boundary → KILLED 2. areArgsCompatible : negated conditional → KILLED |
for (int i = 0; i < args.length; i++) { |
88 |
1
1. areArgsCompatible : negated conditional → KILLED |
if (args[i] == null) |
89 |
1
1. areArgsCompatible : negated conditional → KILLED |
if (formalParameters[i].isPrimitive()) |
90 |
1
1. areArgsCompatible : replaced boolean return with true for com/github/dakusui/pcond/core/refl/MethodSelector$Default::areArgsCompatible → KILLED |
return false; |
91 | else | |
92 | continue; | |
93 |
1
1. areArgsCompatible : negated conditional → KILLED |
if (!isAssignableWithBoxingFrom(formalParameters[i], Utils.toClass(args[i]))) |
94 |
1
1. areArgsCompatible : replaced boolean return with true for com/github/dakusui/pcond/core/refl/MethodSelector$Default::areArgsCompatible → KILLED |
return false; |
95 | } | |
96 |
1
1. areArgsCompatible : replaced boolean return with false for com/github/dakusui/pcond/core/refl/MethodSelector$Default::areArgsCompatible → KILLED |
return true; |
97 | } | |
98 | } | |
99 | ||
100 | /** | |
101 | * A method selector that selects "narrower" methods over "wider" ones. | |
102 | */ | |
103 | class PreferNarrower implements MethodSelector { | |
104 | @Override | |
105 | public List<Method> select(List<Method> methods, Object[] args) { | |
106 |
2
1. select : changed conditional boundary → KILLED 2. select : negated conditional → KILLED |
if (methods.size() < 2) |
107 |
1
1. select : replaced return value with Collections.emptyList for com/github/dakusui/pcond/core/refl/MethodSelector$PreferNarrower::select → KILLED |
return methods; |
108 | List<Method> ret = new LinkedList<>(); | |
109 | for (Method i : methods) { | |
110 |
6
1. lambda$select$0 : replaced boolean return with true for com/github/dakusui/pcond/core/refl/MethodSelector$PreferNarrower::lambda$select$0 → SURVIVED 2. lambda$select$0 : negated conditional → KILLED 3. lambda$select$1 : changed conditional boundary → KILLED 4. lambda$select$1 : negated conditional → KILLED 5. lambda$select$1 : replaced boolean return with true for com/github/dakusui/pcond/core/refl/MethodSelector$PreferNarrower::lambda$select$1 → KILLED 6. select : negated conditional → KILLED |
if (methods.stream().filter(j -> j != i).noneMatch(j -> compareNarrowness(j, i) > 0)) |
111 | ret.add(i); | |
112 | } | |
113 |
1
1. select : replaced return value with Collections.emptyList for com/github/dakusui/pcond/core/refl/MethodSelector$PreferNarrower::select → KILLED |
return ret; |
114 | } | |
115 | ||
116 | @Override | |
117 | public String describe() { | |
118 |
1
1. describe : replaced return value with "" for com/github/dakusui/pcond/core/refl/MethodSelector$PreferNarrower::describe → KILLED |
return "preferNarrower"; |
119 | } | |
120 | ||
121 | /** | |
122 | * If {@code a} is 'narrower' than {@code b}, positive integer will be returned. | |
123 | * If {@code b} is 'narrower' than {@code a}, negative integer will be returned. | |
124 | * Otherwise {@code zero}. | |
125 | * | |
126 | * 'Narrower' means that every parameter of {@code a} is assignable to corresponding | |
127 | * one of {@code b}, but any of {@code b} cannot be assigned to {@code a}'s | |
128 | * corresponding parameter. | |
129 | * | |
130 | * @param a A method. | |
131 | * @param b A method to be compared with {@code a}. | |
132 | * @return a negative integer, zero, or a positive integer as method {@code a} | |
133 | * is less compatible than, as compatible as, or more compatible than | |
134 | * the method {@code b} object. | |
135 | */ | |
136 | private static int compareNarrowness(Method a, Method b) { | |
137 |
2
1. compareNarrowness : negated conditional → SURVIVED 2. compareNarrowness : negated conditional → KILLED |
if (isCompatibleWith(a, b) && isCompatibleWith(b, a)) |
138 | return 0; | |
139 |
2
1. compareNarrowness : negated conditional → KILLED 2. compareNarrowness : negated conditional → KILLED |
if (!isCompatibleWith(a, b) && !isCompatibleWith(b, a)) |
140 | return 0; | |
141 |
2
1. compareNarrowness : negated conditional → KILLED 2. compareNarrowness : replaced int return with 0 for com/github/dakusui/pcond/core/refl/MethodSelector$PreferNarrower::compareNarrowness → KILLED |
return isCompatibleWith(a, b) |
142 | ? -1 | |
143 | : 1; | |
144 | } | |
145 | ||
146 | private static boolean isCompatibleWith(Method a, Method b) { | |
147 |
1
1. isCompatibleWith : removed call to com/github/dakusui/pcond/core/refl/MethodSelector$PreferNarrower::requireSameParameterCounts → KILLED |
requireSameParameterCounts(a, b); |
148 |
1
1. isCompatibleWith : negated conditional → KILLED |
if (Objects.equals(a, b)) |
149 |
1
1. isCompatibleWith : replaced boolean return with false for com/github/dakusui/pcond/core/refl/MethodSelector$PreferNarrower::isCompatibleWith → SURVIVED |
return true; |
150 |
2
1. isCompatibleWith : replaced boolean return with false for com/github/dakusui/pcond/core/refl/MethodSelector$PreferNarrower::isCompatibleWith → KILLED 2. isCompatibleWith : replaced boolean return with true for com/github/dakusui/pcond/core/refl/MethodSelector$PreferNarrower::isCompatibleWith → KILLED |
return IntStream |
151 | .range(0, a.getParameterCount()) | |
152 |
2
1. lambda$isCompatibleWith$2 : replaced boolean return with false for com/github/dakusui/pcond/core/refl/MethodSelector$PreferNarrower::lambda$isCompatibleWith$2 → KILLED 2. lambda$isCompatibleWith$2 : replaced boolean return with true for com/github/dakusui/pcond/core/refl/MethodSelector$PreferNarrower::lambda$isCompatibleWith$2 → KILLED |
.allMatch(i -> isAssignableWithBoxingFrom(a.getParameterTypes()[i], b.getParameterTypes()[i])); |
153 | } | |
154 | ||
155 | private static void requireSameParameterCounts(Method a, Method b) { | |
156 | requireArgument( | |
157 | requireNonNull(a), | |
158 |
2
1. lambda$requireSameParameterCounts$3 : negated conditional → KILLED 2. lambda$requireSameParameterCounts$3 : replaced boolean return with true for com/github/dakusui/pcond/core/refl/MethodSelector$PreferNarrower::lambda$requireSameParameterCounts$3 → KILLED |
(Method v) -> v.getParameterCount() == requireNonNull(b).getParameterCount(), |
159 |
1
1. lambda$requireSameParameterCounts$4 : replaced return value with "" for com/github/dakusui/pcond/core/refl/MethodSelector$PreferNarrower::lambda$requireSameParameterCounts$4 → KILLED |
() -> format("Parameter counts are different: a: %s, b: %s", a, b)); |
160 | } | |
161 | } | |
162 | ||
163 | class PreferExact implements MethodSelector { | |
164 | @Override | |
165 | public List<Method> select(List<Method> methods, Object[] args) { | |
166 |
2
1. select : changed conditional boundary → SURVIVED 2. select : negated conditional → SURVIVED |
if (methods.size() < 2) |
167 |
1
1. select : replaced return value with Collections.emptyList for com/github/dakusui/pcond/core/refl/MethodSelector$PreferExact::select → KILLED |
return methods; |
168 | List<Method> work = methods; | |
169 | for (Object ignored : args) { | |
170 | List<Method> tmp = new ArrayList<>(work); | |
171 |
1
1. select : negated conditional → SURVIVED |
if (!tmp.isEmpty()) { |
172 | work = tmp; | |
173 | break; | |
174 | } | |
175 | } | |
176 |
1
1. select : replaced return value with Collections.emptyList for com/github/dakusui/pcond/core/refl/MethodSelector$PreferExact::select → KILLED |
return work; |
177 | } | |
178 | ||
179 | @Override | |
180 | public String describe() { | |
181 |
1
1. describe : replaced return value with "" for com/github/dakusui/pcond/core/refl/MethodSelector$PreferExact::describe → KILLED |
return "preferExact"; |
182 | } | |
183 | } | |
184 | ||
185 | enum Utils { | |
186 | ; | |
187 | ||
188 | static boolean isAssignableWithBoxingFrom(Class<?> a, Class<?> b) { | |
189 |
1
1. isAssignableWithBoxingFrom : negated conditional → KILLED |
if (a.isAssignableFrom(b)) |
190 |
1
1. isAssignableWithBoxingFrom : replaced boolean return with false for com/github/dakusui/pcond/core/refl/MethodSelector$Utils::isAssignableWithBoxingFrom → KILLED |
return true; |
191 |
2
1. isAssignableWithBoxingFrom : negated conditional → KILLED 2. isAssignableWithBoxingFrom : negated conditional → KILLED |
if (InternalChecks.isPrimitiveWrapperClassOrPrimitive(a) && InternalChecks.isPrimitiveWrapperClassOrPrimitive(b)) |
192 |
2
1. isAssignableWithBoxingFrom : replaced boolean return with true for com/github/dakusui/pcond/core/refl/MethodSelector$Utils::isAssignableWithBoxingFrom → SURVIVED 2. isAssignableWithBoxingFrom : replaced boolean return with false for com/github/dakusui/pcond/core/refl/MethodSelector$Utils::isAssignableWithBoxingFrom → KILLED |
return InternalChecks.isWiderThanOrEqualTo(toWrapperIfPrimitive(a), toWrapperIfPrimitive(b)); |
193 |
1
1. isAssignableWithBoxingFrom : replaced boolean return with true for com/github/dakusui/pcond/core/refl/MethodSelector$Utils::isAssignableWithBoxingFrom → KILLED |
return false; |
194 | } | |
195 | ||
196 | private static Class<?> toWrapperIfPrimitive(Class<?> in) { | |
197 |
1
1. toWrapperIfPrimitive : negated conditional → KILLED |
if (in.isPrimitive()) |
198 |
1
1. toWrapperIfPrimitive : replaced return value with null for com/github/dakusui/pcond/core/refl/MethodSelector$Utils::toWrapperIfPrimitive → KILLED |
return InternalUtils.wrapperClassOf(in); |
199 |
1
1. toWrapperIfPrimitive : replaced return value with null for com/github/dakusui/pcond/core/refl/MethodSelector$Utils::toWrapperIfPrimitive → KILLED |
return in; |
200 | } | |
201 | ||
202 | private static Class<?> toClass(Object value) { | |
203 |
1
1. toClass : replaced return value with null for com/github/dakusui/pcond/core/refl/MethodSelector$Utils::toClass → KILLED |
return value.getClass(); |
204 | } | |
205 | } | |
206 | } | |
Mutations | ||
49 |
1.1 |
|
52 |
1.1 |
|
57 |
1.1 |
|
73 |
1.1 |
|
75 |
1.1 2.2 |
|
81 |
1.1 |
|
85 |
1.1 |
|
86 |
1.1 |
|
87 |
1.1 2.2 |
|
88 |
1.1 |
|
89 |
1.1 |
|
90 |
1.1 |
|
93 |
1.1 |
|
94 |
1.1 |
|
96 |
1.1 |
|
106 |
1.1 2.2 |
|
107 |
1.1 |
|
110 |
1.1 2.2 3.3 4.4 5.5 6.6 |
|
113 |
1.1 |
|
118 |
1.1 |
|
137 |
1.1 2.2 |
|
139 |
1.1 2.2 |
|
141 |
1.1 2.2 |
|
147 |
1.1 |
|
148 |
1.1 |
|
149 |
1.1 |
|
150 |
1.1 2.2 |
|
152 |
1.1 2.2 |
|
158 |
1.1 2.2 |
|
159 |
1.1 |
|
166 |
1.1 2.2 |
|
167 |
1.1 |
|
171 |
1.1 |
|
176 |
1.1 |
|
181 |
1.1 |
|
189 |
1.1 |
|
190 |
1.1 |
|
191 |
1.1 2.2 |
|
192 |
1.1 2.2 |
|
193 |
1.1 |
|
197 |
1.1 |
|
198 |
1.1 |
|
199 |
1.1 |
|
203 |
1.1 |