// *************************************************************************************************************************** | |
// * 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 java.lang.reflect.*; | |
import org.apache.juneau.*; | |
import org.apache.juneau.annotation.*; | |
import org.apache.juneau.reflect.*; | |
/** | |
* Specialized transform for builder classes. | |
* | |
* <ul class='seealso'> | |
* <li class='link'>{@doc juneau-marshall.Transforms.PojoBuilders} | |
* </ul> | |
* | |
* @param <T> The bean class. | |
* @param <B> The builder class. | |
*/ | |
@SuppressWarnings("unchecked") | |
public class BuilderSwap<T,B> { | |
private final Class<T> pojoClass; | |
private final Class<B> builderClass; | |
private final Constructor<T> pojoConstructor; // public Pojo(Builder); | |
private final Constructor<B> builderConstructor; // public Builder(); | |
private final MethodInfo createBuilderMethod; // Builder create(); | |
private final MethodInfo 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<T> pojoClass, Class<B> builderClass, Constructor<T> pojoConstructor, Constructor<B> builderConstructor, MethodInfo createBuilderMethod, MethodInfo 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<T> getPojoClass() { | |
return pojoClass; | |
} | |
/** | |
* The builder class. | |
* | |
* @return The builder class. | |
*/ | |
public Class<B> getBuilderClass() { | |
return builderClass; | |
} | |
/** | |
* Returns the {@link ClassMeta} of the transformed class type. | |
* | |
* <p> | |
* 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 ExecutableException Exception occurred on invoked constructor/method/field. | |
*/ | |
public B create(BeanSession session, ClassMeta<?> hint) throws ExecutableException { | |
if (createBuilderMethod != null) | |
return (B)createBuilderMethod.invoke(null); | |
try { | |
return builderConstructor.newInstance(); | |
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { | |
throw new ExecutableException(e); | |
} | |
} | |
/** | |
* 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 ExecutableException Exception occurred on invoked constructor/method/field. | |
*/ | |
public T build(BeanSession session, B builder, ClassMeta<?> hint) throws ExecutableException { | |
if (createPojoMethod != null) | |
return (T)createPojoMethod.invoke(builder); | |
try { | |
return pojoConstructor.newInstance(builder); | |
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { | |
throw new ExecutableException(e); | |
} | |
} | |
/** | |
* 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 <jk>null</jk> if class wasn't a builder class. | |
*/ | |
@SuppressWarnings("rawtypes") | |
public static BuilderSwap<?,?> findSwapFromBuilderClass(Class<?> builderClass, Visibility cVis, Visibility mVis) { | |
ClassInfo bci = ClassInfo.of(builderClass); | |
if (bci.isNotPublic()) | |
return null; | |
Class<?> pojoClass = ClassInfo.of(builderClass).getParameterType(0, Builder.class); | |
MethodInfo createPojoMethod, createBuilderMethod; | |
ConstructorInfo pojoConstructor; | |
ConstructorInfo builderConstructor; | |
createPojoMethod = bci.getBuilderBuildMethod(); | |
if (createPojoMethod != null) | |
pojoClass = createPojoMethod.getReturnType().inner(); | |
if (pojoClass == null) | |
return null; | |
ClassInfo pci = ClassInfo.of(pojoClass); | |
pojoConstructor = pci.getConstructor(cVis, builderClass); | |
if (pojoConstructor == null) | |
return null; | |
builderConstructor = bci.getNoArgConstructor(cVis); | |
createBuilderMethod = pci.getBuilderCreateMethod(); | |
if (builderConstructor == null && createBuilderMethod == null) | |
return null; | |
return new BuilderSwap(pojoClass, builderClass, pojoConstructor.inner(), builderConstructor == null ? null : builderConstructor.inner(), createBuilderMethod, createPojoMethod); | |
} | |
/** | |
* Creates a BuilderSwap from the specified POJO class if it has one. | |
* | |
* @param bc The bean context to use to look up annotations. | |
* @param pojoClass The POJO class to check. | |
* @param cVis Minimum constructor visibility. | |
* @param mVis Minimum method visibility. | |
* @return A new swap instance, or <jk>null</jk> if class didn't have a builder class. | |
*/ | |
@SuppressWarnings("rawtypes") | |
public static BuilderSwap<?,?> findSwapFromPojoClass(BeanContext bc, Class<?> pojoClass, Visibility cVis, Visibility mVis) { | |
Class<?> builderClass = null; | |
MethodInfo pojoCreateMethod, builderCreateMethod; | |
ConstructorInfo pojoConstructor = null; | |
ConstructorInfo builderConstructor; | |
org.apache.juneau.annotation.Builder b = bc.getAnnotation(org.apache.juneau.annotation.Builder.class, pojoClass); | |
if (b != null && b.value() != Null.class) | |
builderClass = b.value(); | |
ClassInfo pci = ClassInfo.of(pojoClass); | |
builderCreateMethod = pci.getBuilderCreateMethod(); | |
if (builderClass == null && builderCreateMethod != null) | |
builderClass = builderCreateMethod.getReturnType().inner(); | |
if (builderClass == null) { | |
for (ConstructorInfo cc : pci.getPublicConstructors()) { | |
if (cc.isVisible(cVis) && cc.hasNumParams(1)) { | |
ClassInfo pt = cc.getParamType(0); | |
if (pt.isChildOf(Builder.class)) { | |
pojoConstructor = cc; | |
builderClass = pt.inner(); | |
} | |
} | |
} | |
} | |
if (builderClass == null) | |
return null; | |
ClassInfo bci = ClassInfo.of(builderClass); | |
builderConstructor = bci.getNoArgConstructor(cVis); | |
if (builderConstructor == null && builderCreateMethod == null) | |
return null; | |
pojoCreateMethod = bci.getBuilderBuildMethod(); | |
if (pojoConstructor == null) | |
pojoConstructor = pci.getConstructor(cVis, builderClass); | |
if (pojoConstructor == null && pojoCreateMethod == null) | |
return null; | |
return new BuilderSwap(pojoClass, builderClass, pojoConstructor == null ? null : pojoConstructor.inner(), builderConstructor == null ? null : builderConstructor.inner(), builderCreateMethod, pojoCreateMethod); | |
} | |
} |