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.dynamic.scaffold;
17
18 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
19 import net.bytebuddy.description.field.FieldDescription;
20 import net.bytebuddy.description.field.FieldList;
21 import net.bytebuddy.description.type.TypeDefinition;
22 import net.bytebuddy.description.type.TypeDescription;
23 import net.bytebuddy.matcher.ElementMatcher;
24
25 import static net.bytebuddy.matcher.ElementMatchers.*;
26
27 /**
28  * A field locator offers an interface for locating a field that is declared by a specified type.
29  */

30 public interface FieldLocator {
31
32     /**
33      * Locates a field with the given name and throws an exception if no such type exists.
34      *
35      * @param name The name of the field to locate.
36      * @return A resolution for a field lookup.
37      */

38     Resolution locate(String name);
39
40     /**
41      * Locates a field with the given name and type and throws an exception if no such type exists.
42      *
43      * @param name The name of the field to locate.
44      * @param type The type fo the field to locate.
45      * @return A resolution for a field lookup.
46      */

47     Resolution locate(String name, TypeDescription type);
48
49     /**
50      * A resolution for a field lookup.
51      */

52     interface Resolution {
53
54         /**
55          * Returns {@code trueif a field was located.
56          *
57          * @return {@code trueif a field was located.
58          */

59         boolean isResolved();
60
61         /**
62          * Returns the field description if a field was located. This method must only be called if
63          * this resolution was actually resolved.
64          *
65          * @return The located field.
66          */

67         FieldDescription getField();
68
69         /**
70          * An illegal resolution.
71          */

72         enum Illegal implements Resolution {
73
74             /**
75              * The singleton instance.
76              */

77             INSTANCE;
78
79             /**
80              * {@inheritDoc}
81              */

82             public boolean isResolved() {
83                 return false;
84             }
85
86             /**
87              * {@inheritDoc}
88              */

89             public FieldDescription getField() {
90                 throw new IllegalStateException("Could not locate field");
91             }
92         }
93
94         /**
95          * A simple implementation for a field resolution.
96          */

97         @HashCodeAndEqualsPlugin.Enhance
98         class Simple implements Resolution {
99
100             /**
101              * A description of the located field.
102              */

103             private final FieldDescription fieldDescription;
104
105             /**
106              * Creates a new simple resolution for a field.
107              *
108              * @param fieldDescription A description of the located field.
109              */

110             protected Simple(FieldDescription fieldDescription) {
111                 this.fieldDescription = fieldDescription;
112             }
113
114             /**
115              * {@inheritDoc}
116              */

117             public boolean isResolved() {
118                 return true;
119             }
120
121             /**
122              * {@inheritDoc}
123              */

124             public FieldDescription getField() {
125                 return fieldDescription;
126             }
127         }
128     }
129
130     /**
131      * A factory for creating a {@link FieldLocator}.
132      */

133     interface Factory {
134
135         /**
136          * Creates a field locator for a given type.
137          *
138          * @param typeDescription The type for which to create a field locator.
139          * @return A suitable field locator.
140          */

141         FieldLocator make(TypeDescription typeDescription);
142     }
143
144     /**
145      * A field locator that never discovers a field.
146      */

147     enum NoOp implements FieldLocator, Factory {
148
149         /**
150          * The singleton instance.
151          */

152         INSTANCE;
153
154         /**
155          * {@inheritDoc}
156          */

157         public FieldLocator make(TypeDescription typeDescription) {
158             return this;
159         }
160
161         /**
162          * {@inheritDoc}
163          */

164         public Resolution locate(String name) {
165             return Resolution.Illegal.INSTANCE;
166         }
167
168         /**
169          * {@inheritDoc}
170          */

171         public Resolution locate(String name, TypeDescription type) {
172             return Resolution.Illegal.INSTANCE;
173         }
174     }
175
176     /**
177      * An abstract base implementation of a field locator.
178      */

179     @HashCodeAndEqualsPlugin.Enhance
180     abstract class AbstractBase implements FieldLocator {
181
182         /**
183          * The type accessing the field.
184          */

185         protected final TypeDescription accessingType;
186
187         /**
188          * Creates a new field locator.
189          *
190          * @param accessingType The type accessing the field.
191          */

192         protected AbstractBase(TypeDescription accessingType) {
193             this.accessingType = accessingType;
194         }
195
196         /**
197          * {@inheritDoc}
198          */

199         public Resolution locate(String name) {
200             FieldList<?> candidates = locate(named(name).and(isVisibleTo(accessingType)));
201             return candidates.size() == 1
202                     ? new Resolution.Simple(candidates.getOnly())
203                     : Resolution.Illegal.INSTANCE;
204         }
205
206         /**
207          * {@inheritDoc}
208          */

209         public Resolution locate(String name, TypeDescription type) {
210             FieldList<?> candidates = locate(named(name).and(fieldType(type)).and(isVisibleTo(accessingType)));
211             return candidates.size() == 1
212                     ? new Resolution.Simple(candidates.getOnly())
213                     : Resolution.Illegal.INSTANCE;
214         }
215
216         /**
217          * Locates fields that match the given matcher.
218          *
219          * @param matcher The matcher that identifies fields of interest.
220          * @return A list of fields that match the specified matcher.
221          */

222         protected abstract FieldList<?> locate(ElementMatcher<? super FieldDescription> matcher);
223     }
224
225     /**
226      * A field locator that only looks up fields that are declared by a specific type.
227      */

228     @HashCodeAndEqualsPlugin.Enhance
229     class ForExactType extends AbstractBase {
230
231         /**
232          * The type for which to look up fields.
233          */

234         private final TypeDescription typeDescription;
235
236         /**
237          * Creates a new field locator for locating fields from a declared type.
238          *
239          * @param typeDescription The type for which to look up fields that is also providing the accessing type.
240          */

241         public ForExactType(TypeDescription typeDescription) {
242             this(typeDescription, typeDescription);
243         }
244
245         /**
246          * Creates a new field locator for locating fields from a declared type.
247          *
248          * @param typeDescription The type for which to look up fields.
249          * @param accessingType   The accessing type.
250          */

251         public ForExactType(TypeDescription typeDescription, TypeDescription accessingType) {
252             super(accessingType);
253             this.typeDescription = typeDescription;
254         }
255
256         @Override
257         protected FieldList<?> locate(ElementMatcher<? super FieldDescription> matcher) {
258             return typeDescription.getDeclaredFields().filter(matcher);
259         }
260
261         /**
262          * A factory for creating a {@link ForExactType}.
263          */

264         @HashCodeAndEqualsPlugin.Enhance
265         public static class Factory implements FieldLocator.Factory {
266
267             /**
268              * The type for which to locate a field.
269              */

270             private final TypeDescription typeDescription;
271
272             /**
273              * Creates a new factory for a field locator that locates a field for an exact type.
274              *
275              * @param typeDescription The type for which to locate a field.
276              */

277             public Factory(TypeDescription typeDescription) {
278                 this.typeDescription = typeDescription;
279             }
280
281             /**
282              * {@inheritDoc}
283              */

284             public FieldLocator make(TypeDescription typeDescription) {
285                 return new ForExactType(this.typeDescription, typeDescription);
286             }
287         }
288     }
289
290     /**
291      * A field locator that looks up fields that are declared within a class's class hierarchy.
292      */

293     @HashCodeAndEqualsPlugin.Enhance
294     class ForClassHierarchy extends AbstractBase {
295
296         /**
297          * The type for which to look up a field within its class hierarchy.
298          */

299         private final TypeDescription typeDescription;
300
301         /**
302          * Creates a field locator that looks up fields that are declared within a class's class hierarchy.
303          *
304          * @param typeDescription The type for which to look up a field within its class hierarchy which is also the accessing type.
305          */

306         public ForClassHierarchy(TypeDescription typeDescription) {
307             this(typeDescription, typeDescription);
308         }
309
310         /**
311          * Creates a field locator that looks up fields that are declared within a class's class hierarchy.
312          *
313          * @param typeDescription The type for which to look up a field within its class hierarchy.
314          * @param accessingType   The accessing type.
315          */

316         public ForClassHierarchy(TypeDescription typeDescription, TypeDescription accessingType) {
317             super(accessingType);
318             this.typeDescription = typeDescription;
319         }
320
321         @Override
322         protected FieldList<?> locate(ElementMatcher<? super FieldDescription> matcher) {
323             for (TypeDefinition typeDefinition : typeDescription) {
324                 FieldList<?> candidates = typeDefinition.getDeclaredFields().filter(matcher);
325                 if (!candidates.isEmpty()) {
326                     return candidates;
327                 }
328             }
329             return new FieldList.Empty<FieldDescription>();
330         }
331
332         /**
333          * A factory for creating a {@link ForClassHierarchy}.
334          */

335         public enum Factory implements FieldLocator.Factory {
336
337             /**
338              * The singleton instance.
339              */

340             INSTANCE;
341
342             /**
343              * {@inheritDoc}
344              */

345             public FieldLocator make(TypeDescription typeDescription) {
346                 return new ForClassHierarchy(typeDescription);
347             }
348         }
349     }
350
351     /**
352      * A field locator that only locates fields in the top-level type.
353      */

354     class ForTopLevelType extends AbstractBase {
355
356         /**
357          * Creates a new type locator for a top-level type.
358          *
359          * @param typeDescription The type to access.
360          */

361         protected ForTopLevelType(TypeDescription typeDescription) {
362             super(typeDescription);
363         }
364
365         @Override
366         protected FieldList<?> locate(ElementMatcher<? super FieldDescription> matcher) {
367             return accessingType.getDeclaredFields().filter(matcher);
368         }
369
370         /**
371          * A factory for locating a field in a top-level type.
372          */

373         public enum Factory implements FieldLocator.Factory {
374
375             /**
376              * The singleton instance.
377              */

378             INSTANCE;
379
380             /**
381              * {@inheritDoc}
382              */

383             public FieldLocator make(TypeDescription typeDescription) {
384                 return new ForTopLevelType(typeDescription);
385             }
386         }
387     }
388 }
389