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;
17
18 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
19 import net.bytebuddy.description.type.TypeDescription;
20 import net.bytebuddy.utility.RandomString;
21
22 /**
23  * <p>
24  * A naming strategy for determining a fully qualified name for a dynamically created Java type.
25  * </p>
26  * <p>
27  * Note that subclasses that lie within the same package as their superclass can access package-private methods
28  * of super types within the same package.
29  * </p>
30  */

31 public interface NamingStrategy {
32
33     /**
34      * Determines a new name when creating a new type that subclasses the provided type.
35      *
36      * @param superClass The super type of the created type.
37      * @return The name of the dynamic type.
38      */

39     String subclass(TypeDescription.Generic superClass);
40
41     /**
42      * Determines a name for the dynamic type when redefining the provided type.
43      *
44      * @param typeDescription The type being redefined.
45      * @return The name of the dynamic type.
46      */

47     String redefine(TypeDescription typeDescription);
48
49     /**
50      * Determines a name for the dynamic type when rebasing the provided type.
51      *
52      * @param typeDescription The type being redefined.
53      * @return The name of the dynamic type.
54      */

55     String rebase(TypeDescription typeDescription);
56
57     /**
58      * An abstract base implementation where the names of redefined and rebased types are retained.
59      */

60     abstract class AbstractBase implements NamingStrategy {
61
62         /**
63          * {@inheritDoc}
64          */

65         public String subclass(TypeDescription.Generic superClass) {
66             return name(superClass.asErasure());
67         }
68
69         /**
70          * Determines a new name when creating a new type that subclasses the provided type.
71          *
72          * @param superClass The super type of the created type.
73          * @return The name of the dynamic type.
74          */

75         protected abstract String name(TypeDescription superClass);
76
77         /**
78          * {@inheritDoc}
79          */

80         public String redefine(TypeDescription typeDescription) {
81             return typeDescription.getName();
82         }
83
84         /**
85          * {@inheritDoc}
86          */

87         public String rebase(TypeDescription typeDescription) {
88             return typeDescription.getName();
89         }
90     }
91
92     /**
93      * A naming strategy that creates a name by concatenating:
94      * <ol>
95      * <li>The super classes package and name</li>
96      * <li>A given suffix string</li>
97      * <li>A random number</li>
98      * </ol>
99      * Between all these elements, a {@code $} sign is included into the name to improve readability. As an exception,
100      * types that subclass classes from the {@code java.**} packages are prefixed with a given package. This is
101      * necessary as it is illegal to define non-bootstrap classes in this name space. The same strategy is applied
102      * when subclassing a signed type which is equally illegal.
103      */

104     @HashCodeAndEqualsPlugin.Enhance
105     class SuffixingRandom extends AbstractBase {
106
107         /**
108          * The default package for defining types that are renamed to not be contained in the
109          * {@link net.bytebuddy.NamingStrategy.SuffixingRandom#JAVA_PACKAGE} package.
110          */

111         public static final String BYTE_BUDDY_RENAME_PACKAGE = "net.bytebuddy.renamed";
112
113         /**
114          * Indicates that types of the {@code java.*} package should not be prefixed.
115          */

116         public static final String NO_PREFIX = "";
117
118         /**
119          * The package prefix of the {@code java.*} packages for which the definition of
120          * non-bootstrap types is illegal.
121          */

122         private static final String JAVA_PACKAGE = "java.";
123
124         /**
125          * The suffix to attach to a super type name.
126          */

127         private final String suffix;
128
129         /**
130          * The renaming location for types of the {@link net.bytebuddy.NamingStrategy.SuffixingRandom#JAVA_PACKAGE}.
131          */

132         private final String javaLangPackagePrefix;
133
134         /**
135          * An instance for creating random seed values.
136          */

137         @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.IGNORE)
138         private final RandomString randomString;
139
140         /**
141          * A resolver for the base name for naming the unnamed type.
142          */

143         private final BaseNameResolver baseNameResolver;
144
145         /**
146          * Creates an immutable naming strategy with a given suffix but moves types that subclass types within
147          * the {@code java.lang} package into Byte Buddy's package namespace. All names are derived from the
148          * unnamed type's super type.
149          *
150          * @param suffix The suffix for the generated class.
151          */

152         public SuffixingRandom(String suffix) {
153             this(suffix, BaseNameResolver.ForUnnamedType.INSTANCE);
154         }
155
156         /**
157          * Creates an immutable naming strategy with a given suffix but moves types that subclass types within
158          * the {@code java.lang} package into Byte Buddy's package namespace.
159          *
160          * @param suffix                The suffix for the generated class.
161          * @param javaLangPackagePrefix The fallback namespace for type's that subclass types within the
162          *                              {@code java.*} namespace. If The prefix is set to the empty string,
163          *                              no prefix is added.
164          */

165         public SuffixingRandom(String suffix, String javaLangPackagePrefix) {
166             this(suffix, BaseNameResolver.ForUnnamedType.INSTANCE, javaLangPackagePrefix);
167         }
168
169         /**
170          * Creates an immutable naming strategy with a given suffix but moves types that subclass types within
171          * the {@code java.lang} package into Byte Buddy's package namespace.
172          *
173          * @param suffix           The suffix for the generated class.
174          * @param baseNameResolver The base name resolver that is queried for locating the base name.
175          */

176         public SuffixingRandom(String suffix, BaseNameResolver baseNameResolver) {
177             this(suffix, baseNameResolver, BYTE_BUDDY_RENAME_PACKAGE);
178         }
179
180         /**
181          * Creates an immutable naming strategy with a given suffix but moves types that subclass types within
182          * the {@code java.lang} package into a given namespace.
183          *
184          * @param suffix                The suffix for the generated class.
185          * @param baseNameResolver      The base name resolver that is queried for locating the base name.
186          * @param javaLangPackagePrefix The fallback namespace for type's that subclass types within the
187          *                              {@code java.*} namespace. If The prefix is set to the empty string,
188          *                              no prefix is added.
189          */

190         public SuffixingRandom(String suffix, BaseNameResolver baseNameResolver, String javaLangPackagePrefix) {
191             this.suffix = suffix;
192             this.baseNameResolver = baseNameResolver;
193             this.javaLangPackagePrefix = javaLangPackagePrefix;
194             randomString = new RandomString();
195         }
196
197         @Override
198         protected String name(TypeDescription superClass) {
199             String baseName = baseNameResolver.resolve(superClass);
200             if (baseName.startsWith(JAVA_PACKAGE) && !javaLangPackagePrefix.equals("")) {
201                 baseName = javaLangPackagePrefix + "." + baseName;
202             }
203             return baseName + "$" + suffix + "$" + randomString.nextString();
204         }
205
206         /**
207          * A base name resolver is responsible for resolving a name onto which the suffix is appended.
208          */

209         public interface BaseNameResolver {
210
211             /**
212              * Resolves the base name for a given type description.
213              *
214              * @param typeDescription The type for which the base name is resolved.
215              * @return The base name for the given type.
216              */

217             String resolve(TypeDescription typeDescription);
218
219             /**
220              * Uses the unnamed type's super type's name as the resolved name.
221              */

222             enum ForUnnamedType implements BaseNameResolver {
223
224                 /**
225                  * The singleton instance.
226                  */

227                 INSTANCE;
228
229                 /**
230                  * {@inheritDoc}
231                  */

232                 public String resolve(TypeDescription typeDescription) {
233                     return typeDescription.getName();
234                 }
235             }
236
237             /**
238              * Uses a specific type's name as the resolved name.
239              */

240             @HashCodeAndEqualsPlugin.Enhance
241             class ForGivenType implements BaseNameResolver {
242
243                 /**
244                  * The type description which represents the resolved name.
245                  */

246                 private final TypeDescription typeDescription;
247
248                 /**
249                  * Creates a new base name resolver that resolves a using the name of a given type.
250                  *
251                  * @param typeDescription The type description which represents the resolved name.
252                  */

253                 public ForGivenType(TypeDescription typeDescription) {
254                     this.typeDescription = typeDescription;
255                 }
256
257                 /**
258                  * {@inheritDoc}
259                  */

260                 public String resolve(TypeDescription typeDescription) {
261                     return this.typeDescription.getName();
262                 }
263             }
264
265             /**
266              * A base name resolver that simply returns a fixed value.
267              */

268             @HashCodeAndEqualsPlugin.Enhance
269             class ForFixedValue implements BaseNameResolver {
270
271                 /**
272                  * The fixed base name.
273                  */

274                 private final String name;
275
276                 /**
277                  * Creates a new base name resolver for a fixed name.
278                  *
279                  * @param name The fixed name
280                  */

281                 public ForFixedValue(String name) {
282                     this.name = name;
283                 }
284
285                 /**
286                  * {@inheritDoc}
287                  */

288                 public String resolve(TypeDescription typeDescription) {
289                     return name;
290                 }
291             }
292         }
293     }
294
295     /**
296      * A naming strategy that creates a name by prefixing a given class and its package with another package and
297      * by appending a random number to the class's simple name.
298      */

299     @HashCodeAndEqualsPlugin.Enhance
300     class PrefixingRandom extends AbstractBase {
301
302         /**
303          * The package to prefix.
304          */

305         private final String prefix;
306
307         /**
308          * A seed generator.
309          */

310         @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.IGNORE)
311         private final RandomString randomString;
312
313         /**
314          * Creates a new prefixing random naming strategy.
315          *
316          * @param prefix The prefix to append.
317          */

318         public PrefixingRandom(String prefix) {
319             this.prefix = prefix;
320             randomString = new RandomString();
321         }
322
323         @Override
324         protected String name(TypeDescription superClass) {
325             return prefix + "." + superClass.getName() + "$" + randomString.nextString();
326         }
327     }
328 }
329