• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / javabean-tester / 2797

19 Aug 2025 02:31AM UTC coverage: 87.852%. Remained the same
2797

push

github

hazendaz
fix end of file markers

272 of 315 branches covered (86.35%)

499 of 568 relevant lines covered (87.85%)

0.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.15
/src/main/java/com/codebox/builders/ExtensionBuilder.java
1
/*
2
 * JavaBean Tester (https://github.com/hazendaz/javabean-tester)
3
 *
4
 * Copyright 2012-2025 Hazendaz.
5
 *
6
 * All rights reserved. This program and the accompanying materials
7
 * are made available under the terms of The Apache Software License,
8
 * Version 2.0 which accompanies this distribution, and is available at
9
 * http://www.apache.org/licenses/LICENSE-2.0.txt
10
 *
11
 * Contributors:
12
 *     CodeBox (Rob Dawson).
13
 *     Hazendaz (Jeremy Landis).
14
 */
15
package com.codebox.builders;
16

17
import java.util.HashMap;
18
import java.util.Map;
19
import java.util.Map.Entry;
20
import javassist.CannotCompileException;
21
import javassist.ClassPool;
22
import javassist.CtClass;
23
import javassist.CtField;
24
import javassist.CtMethod;
25
import javassist.NotFoundException;
26

27
import org.slf4j.Logger;
28
import org.slf4j.LoggerFactory;
29

30
/**
31
 * ExtensionBuilder dynamically generates a subclass of the given class with additional String properties and their
32
 * getters/setters.
33
 * <p>
34
 * Uses Javassist to create a new class at runtime named <code>clazz.getName() + "Extension"</code>. The generated class
35
 * will have four String properties: jbExtension1, jbExtension2, jbExtension3, jbExtension4.
36
 *
37
 * @param <T>
38
 *            the type to extend
39
 */
40
public class ExtensionBuilder<T> {
1✔
41

42
    /** The Constant logger. */
43
    private static final Logger logger = LoggerFactory.getLogger(ExtensionBuilder.class);
1✔
44

45
    /**
46
     * Generates a dynamic subclass of the given class with additional String properties and their getters/setters.
47
     *
48
     * @param clazz
49
     *            the class to extend
50
     * @return the generated class
51
     * @throws NotFoundException
52
     *             if the class cannot be found in the class pool
53
     * @throws CannotCompileException
54
     *             if the class cannot be compiled
55
     */
56
    public Class<?> generate(final Class<T> clazz) throws NotFoundException, CannotCompileException {
57
        try {
58
            // If extension already recreated, return it
59
            return Class.forName(clazz.getName() + "Extension");
×
60
        } catch (final ClassNotFoundException e) {
1✔
61
            // No extension exists, so create it
62
            ExtensionBuilder.logger.trace("No extension exists, so create it", e);
1✔
63
        }
64

65
        final ClassPool pool = ClassPool.getDefault();
1✔
66
        final CtClass cc = pool.makeClass(clazz.getName() + "Extension");
1✔
67

68
        // add super class
69
        cc.setSuperclass(ExtensionBuilder.resolveCtClass(clazz));
1✔
70

71
        final Map<String, Class<?>> properties = new HashMap<>();
1✔
72
        properties.put("jbExtension1", String.class);
1✔
73
        properties.put("jbExtension2", String.class);
1✔
74
        properties.put("jbExtension3", String.class);
1✔
75
        properties.put("jbExtension4", String.class);
1✔
76

77
        for (final Entry<String, Class<?>> entry : properties.entrySet()) {
1✔
78

79
            // Add field
80
            cc.addField(new CtField(ExtensionBuilder.resolveCtClass(entry.getValue()), entry.getKey(), cc));
1✔
81

82
            // Add getter
83
            cc.addMethod(ExtensionBuilder.generateGetter(cc, entry.getKey(), entry.getValue()));
1✔
84

85
            // Add setter
86
            cc.addMethod(ExtensionBuilder.generateSetter(cc, entry.getKey(), entry.getValue()));
1✔
87
        }
1✔
88

89
        return cc.toClass();
1✔
90
    }
91

92
    /**
93
     * Generates a getter method for the specified field.
94
     *
95
     * @param declaringClass
96
     *            the class to add the method to
97
     * @param fieldName
98
     *            the name of the field
99
     * @param fieldClass
100
     *            the type of the field
101
     * @return the generated getter method
102
     * @throws CannotCompileException
103
     *             if the method cannot be compiled
104
     */
105
    private static CtMethod generateGetter(final CtClass declaringClass, final String fieldName,
106
            final Class<?> fieldClass) throws CannotCompileException {
107
        String methodSrc = """
1✔
108
                public %s get%s() {
109
                    return this.%s;
110
                }
111
                """.formatted(fieldClass.getName(), fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1),
1✔
112
                fieldName);
113
        return CtMethod.make(methodSrc, declaringClass);
1✔
114
    }
115

116
    /**
117
     * Generates a setter method for the specified field.
118
     *
119
     * @param declaringClass
120
     *            the class to add the method to
121
     * @param fieldName
122
     *            the name of the field
123
     * @param fieldClass
124
     *            the type of the field
125
     * @return the generated setter method
126
     * @throws CannotCompileException
127
     *             if the method cannot be compiled
128
     */
129
    private static CtMethod generateSetter(final CtClass declaringClass, final String fieldName,
130
            final Class<?> fieldClass) throws CannotCompileException {
131
        String methodSrc = """
1✔
132
                public void set%s(%s %s) {
133
                    this.%s = %s;
134
                }
135
                """.formatted(fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1), fieldClass.getName(),
1✔
136
                fieldName, fieldName, fieldName);
137
        return CtMethod.make(methodSrc, declaringClass);
1✔
138
    }
139

140
    /**
141
     * Resolves a CtClass from a Java Class using the default class pool.
142
     *
143
     * @param clazz
144
     *            the Java class to resolve
145
     * @return the corresponding CtClass
146
     * @throws NotFoundException
147
     *             if the class cannot be found in the class pool
148
     */
149
    private static CtClass resolveCtClass(final Class<?> clazz) throws NotFoundException {
150
        return ClassPool.getDefault().get(clazz.getName());
1✔
151
    }
152

153
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc