3030 *
3131 * <p>The class contains many attributes that are helpful for use in MyBatis and Spring runtime
3232 * environments, but the only required attributes are the name of the column and a reference to
33- * the SqlTable the column is a part of.
33+ * the {@link SqlTable} the column is a part of.
3434 *
3535 * <p>The class can be extended if you wish to associate additional attributes with a column for your
36- * own purposes. Extending the class involves the following activities:
36+ * own purposes. Extending the class is a bit more challenging than you might expect because you will need to
37+ * handle the covariant types for many methods in {@code SqlColumn}. Additionally, many methods in {@code SqlColumn}
38+ * create new instances of the class in keeping with the library's primary strategy of immutability. You will also
39+ * need to ensure that these methods create instances of your extended class, rather than the base {@code SqlColumn}
40+ * class. We have worked to keep this process as simple as possible.
41+ *
42+ * <p>Extending the class involves the following activities:
3743 * <ol>
3844 * <li>Create a class that extends {@link SqlColumn}</li>
3945 * <li>In your extended class, create a static builder class that extends {@link SqlColumn.AbstractBuilder}</li>
4046 * <li>Add your desired attributes to the class and the builder</li>
47+ * <li>In your extended class, override the {@link SqlColumn#copyBuilder()} method and return a new instance of
48+ * your builder with all attributes set. You should call the
49+ * {@link SqlColumn#populateBaseBuilder(AbstractBuilder)} method
50+ * to set the attributes from {@code SqlColumn}, then populate your extended attributes.
51+ * </li>
4152 * <li>You MUST override the following methods. These methods are used with regular operations in the library.
4253 * If you do not override these methods, it is likely that your extended attributes will be lost during
43- * regular usage. For example, if a user calls the {@code as} method to apply an alias, the base
44- * {@code SqlColumn} class will create a new instance of {@code SqlColumn}, NOT your extended class.
54+ * regular usage. For example, if you do not override the {@code as} method and a user calls the method to
55+ * apply an alias, then the base {@code SqlColumn} class would create a new instance of {@code SqlColumn}, NOT
56+ * your extended class.
4557 * <ul>
4658 * <li>{@link SqlColumn#as(String)}</li>
4759 * <li>{@link SqlColumn#asCamelCase()}</li>
6375 * </li>
6476 * </ol>
6577 *
66- * <p>The test code for this library contains an example of a proper extension of this class. In most cases, the code
67- * for the overriding methods can be simply copied from this class and then the return types can be changed to the
68- * subclass's covariant type (this pre-supposes that you create a {@code copyBuilder} method that returns a new builder
69- * populated with all current class attributes).
78+ * <p>For all overridden methods except {@code copyBuilder()}, the process is to call the superclass
79+ * method and cast the result properly. We provide a {@link SqlColumn#cast(SqlColumn)} method to aid with this
80+ * process. For example, overriding the {@code descending} method could look like this:
81+ *
82+ * <p>
83+ * <pre>
84+ * {@code
85+ * @Override
86+ * public MyExtendedColumn<T> descending() {
87+ * return cast(super.descending());
88+ * }
89+ * }
90+ * </pre>
91+ *
92+ * <p>The test code for this library contains an example of a proper extension of this class.
7093 *
7194 * @param <T> the Java type associated with the column
7295 */
@@ -84,7 +107,7 @@ public class SqlColumn<T> implements BindableColumn<T>, SortSpecification {
84107 protected final @ Nullable Class <T > javaType ;
85108 protected final @ Nullable String javaProperty ;
86109
87- protected SqlColumn (AbstractBuilder <T , ?> builder ) {
110+ protected SqlColumn (AbstractBuilder <T , ?, ? > builder ) {
88111 name = Objects .requireNonNull (builder .name );
89112 table = Objects .requireNonNull (builder .table );
90113 jdbcType = builder .jdbcType ;
@@ -142,11 +165,7 @@ public Optional<String> javaProperty() {
142165 */
143166 @ Override
144167 public SqlColumn <T > descending () {
145- return setDescending (copyBuilder ()).build ();
146- }
147- 148- protected <B extends AbstractBuilder <T , ?>> B setDescending (B builder ) {
149- return cast (builder .withDescendingPhrase (" DESC" )); //$NON-NLS-1$
168+ return cast (copyBuilder ().withDescendingPhrase (" DESC" ).build ()); //$NON-NLS-1$
150169 }
151170
152171 /**
@@ -159,11 +178,7 @@ public SqlColumn<T> descending() {
159178 */
160179 @ Override
161180 public SqlColumn <T > as (String alias ) {
162- return setAlias (copyBuilder (), alias ).build ();
163- }
164- 165- protected <B extends AbstractBuilder <T , ?>> B setAlias (B builder , String alias ) {
166- return cast (builder .withAlias (alias ));
181+ return cast (copyBuilder ().withAlias (alias ).build ());
167182 }
168183
169184 /**
@@ -174,11 +189,7 @@ public SqlColumn<T> as(String alias) {
174189 * @return a new column that will be rendered with the specified table qualifier
175190 */
176191 public SqlColumn <T > qualifiedWith (String tableQualifier ) {
177- return setTableQualifier (copyBuilder (), tableQualifier ).build ();
178- }
179- 180- protected <B extends AbstractBuilder <T , ?>> B setTableQualifier (B builder , String tableQualifier ) {
181- return cast (builder .withTableQualifier (tableQualifier ));
192+ return cast (copyBuilder ().withTableQualifier (tableQualifier ).build ());
182193 }
183194
184195 /**
@@ -193,11 +204,9 @@ public SqlColumn<T> qualifiedWith(String tableQualifier) {
193204 * @return a new column aliased with a camel case version of the column name
194205 */
195206 public SqlColumn <T > asCamelCase () {
196- return setCamelCaseAlias (copyBuilder ()).build ();
197- }
198- 199- protected <B extends AbstractBuilder <T , ?>> B setCamelCaseAlias (B builder ) {
200- return cast (builder .withAlias ("\" " + StringUtilities .toCamelCase (name ) + "\" " )); //$NON-NLS-1$ //$NON-NLS-2$
207+ return cast (copyBuilder ()
208+ .withAlias ("\" " + StringUtilities .toCamelCase (name ) + "\" " ) //$NON-NLS-1$ //$NON-NLS-2$
209+ .build ());
201210 }
202211
203212 @ Override
@@ -310,7 +319,7 @@ public <S> SqlColumn<S> withJavaProperty(String javaProperty) {
310319 return cast (copyBuilder ().withJavaProperty (javaProperty ).build ());
311320 }
312321
313- private Builder < T > copyBuilder () {
322+ protected AbstractBuilder < T , ?, ? > copyBuilder () {
314323 return populateBaseBuilder (new Builder <>());
315324 }
316325
@@ -319,11 +328,6 @@ protected <S extends SqlColumn<?>> S cast(SqlColumn<?> column) {
319328 return (S ) column ;
320329 }
321330
322- @ SuppressWarnings ("unchecked" )
323- protected <B extends AbstractBuilder <?, ?>> B cast (AbstractBuilder <?, ?> builder ) {
324- return (B ) builder ;
325- }
326- 327331 /**
328332 * This method will add all current attributes to the specified builder. It is useful when creating
329333 * new class instances that only change one attribute - we set all current attributes, then
@@ -334,7 +338,7 @@ protected <S extends SqlColumn<?>> S cast(SqlColumn<?> column) {
334338 * @return the populated builder
335339 */
336340 @ SuppressWarnings ("unchecked" )
337- protected <B extends AbstractBuilder <T , ?>> B populateBaseBuilder (B builder ) {
341+ protected <B extends AbstractBuilder <T , ?, ? >> B populateBaseBuilder (B builder ) {
338342 return (B ) builder
339343 .withName (this .name )
340344 .withTable (this .table )
@@ -362,7 +366,7 @@ public static <T> SqlColumn<T> of(String name, SqlTable table, JDBCType jdbcType
362366 .build ();
363367 }
364368
365- public static abstract class AbstractBuilder <T , B extends AbstractBuilder <T , B >> {
369+ public static abstract class AbstractBuilder <T , C extends SqlColumn < T >, B extends AbstractBuilder <T , C , B >> {
366370 protected @ Nullable String name ;
367371 protected @ Nullable SqlTable table ;
368372 protected @ Nullable JDBCType jdbcType ;
@@ -431,9 +435,12 @@ public B withJavaProperty(@Nullable String javaProperty) {
431435 }
432436
433437 protected abstract B getThis ();
438+ 439+ public abstract C build ();
434440 }
435441
436- public static class Builder <T > extends AbstractBuilder <T , Builder <T >> {
442+ public static class Builder <T > extends AbstractBuilder <T , SqlColumn <T >, Builder <T >> {
443+ @ Override
437444 public SqlColumn <T > build () {
438445 return new SqlColumn <>(this );
439446 }
0 commit comments