All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.juneau.transform.BuilderSwap Maven / Gradle / Ivy

There is a newer version: 9.0.1
Show newest version
// ***************************************************************************************************************************
// * 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:
*
    *
* * @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; } }