1 /*
2 * Copyright 2014 - 2020 Rafael Winterhalter
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package net.bytebuddy.matcher;
17
18 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
19 import net.bytebuddy.description.field.FieldDescription;
20 import net.bytebuddy.description.method.MethodDescription;
21 import net.bytebuddy.description.type.RecordComponentDescription;
22 import net.bytebuddy.description.type.TypeDescription;
23
24 import java.util.Arrays;
25 import java.util.List;
26
27 import static net.bytebuddy.matcher.ElementMatchers.*;
28
29 /**
30 * A latent matcher that resolves an {@link ElementMatcher} after supplying a type description.
31 *
32 * @param <T> The type of the matched element.
33 */
34 public interface LatentMatcher<T> {
35
36 /**
37 * Resolves the element matcher this instance represents for the supplied type description.
38 *
39 * @param typeDescription The type description for which the represented matcher should be resolved.
40 * @return An {@link ElementMatcher} that represents this matcher's resolved form.
41 */
42 ElementMatcher<? super T> resolve(TypeDescription typeDescription);
43
44 /**
45 * A latent matching methods that are declared by the resolved type.
46 */
47 enum ForSelfDeclaredMethod implements LatentMatcher<MethodDescription> {
48
49 /**
50 * Matches any method declared by the resolved type.
51 */
52 DECLARED(false),
53
54 /**
55 * Matches any method not declared by the resolved type.
56 */
57 NOT_DECLARED(true);
58
59 /**
60 * {@code true} if the matcher is inverted.
61 */
62 private final boolean inverted;
63
64 /**
65 * Creates a new latent matcher for a self-declared method.
66 *
67 * @param inverted {@code true} if the matcher is inverted.
68 */
69 ForSelfDeclaredMethod(boolean inverted) {
70 this.inverted = inverted;
71 }
72
73 /**
74 * {@inheritDoc}
75 */
76 @SuppressWarnings("unchecked")
77 public ElementMatcher<? super MethodDescription> resolve(TypeDescription typeDescription) {
78 // Casting is required by some Java 6 compilers.
79 return (ElementMatcher<? super MethodDescription>) (inverted
80 ? not(isDeclaredBy(typeDescription))
81 : isDeclaredBy(typeDescription));
82 }
83 }
84
85 /**
86 * A latent matcher representing an already resolved {@link ElementMatcher}.
87 *
88 * @param <S> The type of the matched element.
89 */
90 @HashCodeAndEqualsPlugin.Enhance
91 class Resolved<S> implements LatentMatcher<S> {
92
93 /**
94 * The resolved matcher.
95 */
96 private final ElementMatcher<? super S> matcher;
97
98 /**
99 * Creates a new resolved latent matcher.
100 *
101 * @param matcher The resolved matcher.
102 */
103 public Resolved(ElementMatcher<? super S> matcher) {
104 this.matcher = matcher;
105 }
106
107 /**
108 * {@inheritDoc}
109 */
110 public ElementMatcher<? super S> resolve(TypeDescription typeDescription) {
111 return matcher;
112 }
113 }
114
115 /**
116 * A latent matcher where the field token is being attached to the supplied type description before matching.
117 */
118 @HashCodeAndEqualsPlugin.Enhance
119 class ForFieldToken implements LatentMatcher<FieldDescription> {
120
121 /**
122 * A token representing the field being matched.
123 */
124 private final FieldDescription.Token token;
125
126 /**
127 * Creates a new latent matcher for a field token.
128 *
129 * @param token A token representing the field being matched.
130 */
131 public ForFieldToken(FieldDescription.Token token) {
132 this.token = token;
133 }
134
135 /**
136 * {@inheritDoc}
137 */
138 public ElementMatcher<? super FieldDescription> resolve(TypeDescription typeDescription) {
139 return new ResolvedMatcher(token.asSignatureToken(typeDescription));
140 }
141
142 /**
143 * A resolved matcher of a latent field matcher for a field token.
144 */
145 @HashCodeAndEqualsPlugin.Enhance
146 protected static class ResolvedMatcher implements ElementMatcher<FieldDescription> {
147
148 /**
149 * The signature token representing the matched field.
150 */
151 private final FieldDescription.SignatureToken signatureToken;
152
153 /**
154 * Creates a new resolved matcher.
155 *
156 * @param signatureToken The signature token representing the matched field.
157 */
158 protected ResolvedMatcher(FieldDescription.SignatureToken signatureToken) {
159 this.signatureToken = signatureToken;
160 }
161
162 /**
163 * {@inheritDoc}
164 */
165 public boolean matches(FieldDescription target) {
166 return target.asSignatureToken().equals(signatureToken);
167 }
168 }
169 }
170
171 /**
172 * A latent matcher where the method token is being attached to the supplied type description before matching.
173 */
174 @HashCodeAndEqualsPlugin.Enhance
175 class ForMethodToken implements LatentMatcher<MethodDescription> {
176
177 /**
178 * A token representing the method being matched.
179 */
180 private final MethodDescription.Token token;
181
182 /**
183 * Creates a new latent matcher for a method token.
184 *
185 * @param token A token representing the method being matched.
186 */
187 public ForMethodToken(MethodDescription.Token token) {
188 this.token = token;
189 }
190
191 /**
192 * {@inheritDoc}
193 */
194 public ElementMatcher<? super MethodDescription> resolve(TypeDescription typeDescription) {
195 return new ResolvedMatcher(token.asSignatureToken(typeDescription));
196 }
197
198 /**
199 * A resolved matcher of a latent method matcher for a method token.
200 */
201 @HashCodeAndEqualsPlugin.Enhance
202 protected static class ResolvedMatcher implements ElementMatcher<MethodDescription> {
203
204 /**
205 * The signature token representing the matched field.
206 */
207 private final MethodDescription.SignatureToken signatureToken;
208
209 /**
210 * Creates a new resolved matcher.
211 *
212 * @param signatureToken The signature token representing the matched field.
213 */
214 protected ResolvedMatcher(MethodDescription.SignatureToken signatureToken) {
215 this.signatureToken = signatureToken;
216 }
217
218 /**
219 * {@inheritDoc}
220 */
221 public boolean matches(MethodDescription target) {
222 return target.asSignatureToken().equals(signatureToken);
223 }
224 }
225 }
226
227 /**
228 * A latent matcher for a record component token.
229 */
230 @HashCodeAndEqualsPlugin.Enhance
231 class ForRecordComponentToken implements LatentMatcher<RecordComponentDescription> {
232
233 /**
234 * The token being matched.
235 */
236 private final RecordComponentDescription.Token token;
237
238 /**
239 * Creates a latent matcher for a record component token.
240 *
241 * @param token The token being matched.
242 */
243 public ForRecordComponentToken(RecordComponentDescription.Token token) {
244 this.token = token;
245 }
246
247 /**
248 * {@inheritDoc}
249 */
250 public ElementMatcher<? super RecordComponentDescription> resolve(TypeDescription typeDescription) {
251 return ElementMatchers.<RecordComponentDescription>named(token.getName());
252 }
253 }
254
255 /**
256 * A matcher that computes the conjunction of all supplied latent matchers.
257 *
258 * @param <S> The type of the matched element.
259 */
260 @HashCodeAndEqualsPlugin.Enhance
261 class Conjunction<S> implements LatentMatcher<S> {
262
263 /**
264 * The matchers this conjunction represents.
265 */
266 private final List<? extends LatentMatcher<? super S>> matchers;
267
268 /**
269 * Creates a new conjunction of latent matchers.
270 *
271 * @param matcher The matchers this conjunction represents.
272 */
273 @SuppressWarnings("unchecked") // In absence of @SafeVarargs
274 public Conjunction(LatentMatcher<? super S>... matcher) {
275 this(Arrays.asList(matcher));
276 }
277
278 /**
279 * Creates a new conjunction of latent matchers.
280 *
281 * @param matchers The matchers this conjunction represents.
282 */
283 public Conjunction(List<? extends LatentMatcher<? super S>> matchers) {
284 this.matchers = matchers;
285 }
286
287 /**
288 * {@inheritDoc}
289 */
290 public ElementMatcher<? super S> resolve(TypeDescription typeDescription) {
291 ElementMatcher.Junction<S> matcher = any();
292 for (LatentMatcher<? super S> latentMatcher : matchers) {
293 matcher = matcher.and(latentMatcher.resolve(typeDescription));
294 }
295 return matcher;
296 }
297 }
298
299 /**
300 * A matcher that computes the disjunction of all supplied latent matchers.
301 *
302 * @param <S> The type of the matched element.
303 */
304 @HashCodeAndEqualsPlugin.Enhance
305 class Disjunction<S> implements LatentMatcher<S> {
306
307 /**
308 * The matchers this disjunction represents.
309 */
310 private final List<? extends LatentMatcher<? super S>> matchers;
311
312 /**
313 * Creates a new disjunction of latent matchers.
314 *
315 * @param matcher The matchers this disjunction represents.
316 */
317 @SuppressWarnings("unchecked") // In absence of @SafeVarargs
318 public Disjunction(LatentMatcher<? super S>... matcher) {
319 this(Arrays.asList(matcher));
320 }
321
322 /**
323 * Creates a new disjunction of latent matchers.
324 *
325 * @param matchers The matchers this disjunction represents.
326 */
327 public Disjunction(List<? extends LatentMatcher<? super S>> matchers) {
328 this.matchers = matchers;
329 }
330
331 /**
332 * {@inheritDoc}
333 */
334 public ElementMatcher<? super S> resolve(TypeDescription typeDescription) {
335 ElementMatcher.Junction<S> matcher = none();
336 for (LatentMatcher<? super S> latentMatcher : matchers) {
337 matcher = matcher.or(latentMatcher.resolve(typeDescription));
338 }
339 return matcher;
340 }
341 }
342 }
343