org.apache.juneau.transform.BuilderSwap Maven / Gradle / Ivy
// ***************************************************************************************************************************
// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file *
// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file *
// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance *
// * with the License. You may obtain a copy of the License at *
// * *
// * http://www.apache.org/licenses/LICENSE-2.0 *
// * *
// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an *
// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the *
// * specific language governing permissions and limitations under the License. *
// ***************************************************************************************************************************
package org.apache.juneau.transform;
import static org.apache.juneau.internal.ClassFlags.*;
import static org.apache.juneau.internal.ClassUtils.*;
import java.lang.reflect.*;
import org.apache.juneau.*;
import org.apache.juneau.annotation.*;
/**
* Specialized transform for builder classes.
*
* See Also:
*
* - {@doc juneau-marshall.Transforms.PojoBuilders}
*
*
* @param The bean class.
* @param The builder class.
*/
@SuppressWarnings("unchecked")
public class BuilderSwap {
private final Class pojoClass;
private final Class builderClass;
private final Constructor pojoConstructor; // public Pojo(Builder);
private final Constructor builderConstructor; // public Builder();
private final Method createBuilderMethod; // Builder create();
private final Method createPojoMethod; // Pojo build();
private ClassMeta> builderClassMeta;
/**
* Constructor.
*
* @param pojoClass The POJO class created by the builder class.
* @param builderClass The builder class.
* @param pojoConstructor The POJO constructor that takes in a builder as a parameter.
* @param builderConstructor The builder no-arg constructor.
* @param createBuilderMethod The static create() method on the POJO class.
* @param createPojoMethod The build() method on the builder class.
*/
protected BuilderSwap(Class pojoClass, Class builderClass, Constructor pojoConstructor, Constructor builderConstructor, Method createBuilderMethod, Method createPojoMethod) {
this.pojoClass = pojoClass;
this.builderClass = builderClass;
this.pojoConstructor = pojoConstructor;
this.builderConstructor = builderConstructor;
this.createBuilderMethod = createBuilderMethod;
this.createPojoMethod = createPojoMethod;
}
/**
* The POJO class.
*
* @return The POJO class.
*/
public Class getPojoClass() {
return pojoClass;
}
/**
* The builder class.
*
* @return The builder class.
*/
public Class getBuilderClass() {
return builderClass;
}
/**
* Returns the {@link ClassMeta} of the transformed class type.
*
*
* This value is cached for quick lookup.
*
* @param session
* The bean context to use to get the class meta.
* This is always going to be the same bean context that created this swap.
* @return The {@link ClassMeta} of the transformed class type.
*/
public ClassMeta> getBuilderClassMeta(BeanSession session) {
if (builderClassMeta == null)
builderClassMeta = session.getClassMeta(getBuilderClass());
return builderClassMeta;
}
/**
* Creates a new builder object.
*
* @param session The current bean session.
* @param hint A hint about the class type.
* @return A new POJO.
* @throws Exception
*/
public B create(BeanSession session, ClassMeta> hint) throws Exception {
if (createBuilderMethod != null)
return (B)createBuilderMethod.invoke(null);
return builderConstructor.newInstance();
}
/**
* Creates a new POJO from the specified builder.
*
* @param session The current bean session.
* @param builder The POJO builder.
* @param hint A hint about the class type.
* @return A new POJO.
* @throws Exception
*/
public T build(BeanSession session, B builder, ClassMeta> hint) throws Exception {
if (createPojoMethod != null)
return (T)createPojoMethod.invoke(builder);
return pojoConstructor.newInstance(builder);
}
/**
* Creates a BuilderSwap from the specified builder class if it qualifies as one.
*
* @param builderClass The potential builder class.
* @param cVis Minimum constructor visibility.
* @param mVis Minimum method visibility.
* @return A new swap instance, or null if class wasn't a builder class.
*/
@SuppressWarnings("rawtypes")
public static BuilderSwap,?> findSwapFromBuilderClass(Class> builderClass, Visibility cVis, Visibility mVis) {
if (! isPublic(builderClass))
return null;
Class> pojoClass = resolveParameterType(Builder.class, 0, builderClass);
Method createPojoMethod, createBuilderMethod;
Constructor> pojoConstructor, builderConstructor;
createPojoMethod = findCreatePojoMethod(builderClass);
if (createPojoMethod != null)
pojoClass = createPojoMethod.getReturnType();
if (pojoClass == null)
return null;
pojoConstructor = findConstructor(pojoClass, cVis, false, builderClass);
if (pojoConstructor == null)
return null;
builderConstructor = findNoArgConstructor(builderClass, cVis);
createBuilderMethod = findBuilderCreateMethod(pojoClass);
if (builderConstructor == null && createBuilderMethod == null)
return null;
return new BuilderSwap(pojoClass, builderClass, pojoConstructor, builderConstructor, createBuilderMethod, createPojoMethod);
}
/**
* Creates a BuilderSwap from the specified POJO class if it has one.
*
* @param pojoClass The POJO class to check.
* @param cVis Minimum constructor visibility.
* @param mVis Minimum method visibility.
* @return A new swap instance, or null if class didn't have a builder class.
*/
@SuppressWarnings("rawtypes")
public static BuilderSwap,?> findSwapFromPojoClass(Class> pojoClass, Visibility cVis, Visibility mVis) {
Class> builderClass = null;
Method pojoCreateMethod, builderCreateMethod;
Constructor> pojoConstructor = null, builderConstructor;
org.apache.juneau.annotation.Builder b = pojoClass.getAnnotation(org.apache.juneau.annotation.Builder.class);
if (b != null && b.value() != Null.class)
builderClass = b.value();
builderCreateMethod = findBuilderCreateMethod(pojoClass);
if (builderClass == null && builderCreateMethod != null)
builderClass = builderCreateMethod.getReturnType();
if (builderClass == null) {
for (Constructor cc : pojoClass.getConstructors()) {
if (cVis.isVisible(cc) && hasNumArgs(cc, 1)) {
Class>[] pt = cc.getParameterTypes();
if (isParentClass(Builder.class, pt[0])) {
pojoConstructor = cc;
builderClass = pt[0];
}
}
}
}
if (builderClass == null)
return null;
builderConstructor = findNoArgConstructor(builderClass, cVis);
if (builderConstructor == null && builderCreateMethod == null)
return null;
pojoCreateMethod = findCreatePojoMethod(builderClass);
if (pojoConstructor == null)
pojoConstructor = findConstructor(pojoClass, cVis, false, builderClass);
if (pojoConstructor == null && pojoCreateMethod == null)
return null;
return new BuilderSwap(pojoClass, builderClass, pojoConstructor, builderConstructor, builderCreateMethod, pojoCreateMethod);
}
private static Method findBuilderCreateMethod(Class> pojoClass) {
for (Method m : pojoClass.getDeclaredMethods())
if (isAll(m, PUBLIC, STATIC) && hasName(m, "create") && ! hasReturnType(m, Void.class))
return m;
return null;
}
private static Method findCreatePojoMethod(Class> builderClass) {
for (Method m : builderClass.getDeclaredMethods())
if (isAll(m, NOT_STATIC) && hasName(m, "build") && ! hasReturnType(m, Void.class))
return m;
return null;
}
}