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