001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.transform;
014
015import static org.apache.juneau.internal.ClassUtils.*;
016
017import java.lang.reflect.*;
018
019import org.apache.juneau.*;
020import org.apache.juneau.annotation.*;
021import org.apache.juneau.reflect.*;
022
023/**
024 * Specialized transform for builder classes.
025 *
026 * <ul class='seealso'>
027 *    <li class='link'>{@doc juneau-marshall.Transforms.PojoBuilders}
028 * </ul>
029 *
030 * @param <T> The bean class.
031 * @param <B> The builder class.
032 */
033@SuppressWarnings("unchecked")
034public class BuilderSwap<T,B> {
035
036   private final Class<T> pojoClass;
037   private final Class<B> builderClass;
038   private final Constructor<T> pojoConstructor;      // public Pojo(Builder);
039   private final Constructor<B> builderConstructor;   // public Builder();
040   private final MethodInfo createBuilderMethod;          // Builder create();
041   private final MethodInfo createPojoMethod;             // Pojo build();
042   private ClassMeta<?> builderClassMeta;
043
044   /**
045    * Constructor.
046    *
047    * @param pojoClass The POJO class created by the builder class.
048    * @param builderClass The builder class.
049    * @param pojoConstructor The POJO constructor that takes in a builder as a parameter.
050    * @param builderConstructor The builder no-arg constructor.
051    * @param createBuilderMethod The static create() method on the POJO class.
052    * @param createPojoMethod The build() method on the builder class.
053    */
054   protected BuilderSwap(Class<T> pojoClass, Class<B> builderClass, Constructor<T> pojoConstructor, Constructor<B> builderConstructor, MethodInfo createBuilderMethod, MethodInfo createPojoMethod) {
055      this.pojoClass = pojoClass;
056      this.builderClass = builderClass;
057      this.pojoConstructor = pojoConstructor;
058      this.builderConstructor = builderConstructor;
059      this.createBuilderMethod = createBuilderMethod;
060      this.createPojoMethod = createPojoMethod;
061   }
062
063   /**
064    * The POJO class.
065    *
066    * @return The POJO class.
067    */
068   public Class<T> getPojoClass() {
069      return pojoClass;
070   }
071
072   /**
073    * The builder class.
074    *
075    * @return The builder class.
076    */
077   public Class<B> getBuilderClass() {
078      return builderClass;
079   }
080
081   /**
082    * Returns the {@link ClassMeta} of the transformed class type.
083    *
084    * <p>
085    * This value is cached for quick lookup.
086    *
087    * @param session
088    *    The bean context to use to get the class meta.
089    *    This is always going to be the same bean context that created this swap.
090    * @return The {@link ClassMeta} of the transformed class type.
091    */
092   public ClassMeta<?> getBuilderClassMeta(BeanSession session) {
093      if (builderClassMeta == null)
094         builderClassMeta = session.getClassMeta(getBuilderClass());
095      return builderClassMeta;
096   }
097
098   /**
099    * Creates a new builder object.
100    *
101    * @param session The current bean session.
102    * @param hint A hint about the class type.
103    * @return A new POJO.
104    * @throws ExecutableException Exception occurred on invoked constructor/method/field.
105    */
106   public B create(BeanSession session, ClassMeta<?> hint) throws ExecutableException {
107      if (createBuilderMethod != null)
108         return (B)createBuilderMethod.invoke(null);
109      try {
110         return builderConstructor.newInstance();
111      } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
112         throw new ExecutableException(e);
113      }
114   }
115
116   /**
117    * Creates a new POJO from the specified builder.
118    *
119    * @param session The current bean session.
120    * @param builder The POJO builder.
121    * @param hint A hint about the class type.
122    * @return A new POJO.
123    * @throws ExecutableException Exception occurred on invoked constructor/method/field.
124    */
125   public T build(BeanSession session, B builder, ClassMeta<?> hint) throws ExecutableException {
126      if (createPojoMethod != null)
127         return (T)createPojoMethod.invoke(builder);
128      try {
129         return pojoConstructor.newInstance(builder);
130      } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
131         throw new ExecutableException(e);
132      }
133   }
134
135   /**
136    * Creates a BuilderSwap from the specified builder class if it qualifies as one.
137    *
138    * @param builderClass The potential builder class.
139    * @param cVis Minimum constructor visibility.
140    * @param mVis Minimum method visibility.
141    * @return A new swap instance, or <jk>null</jk> if class wasn't a builder class.
142    */
143   @SuppressWarnings("rawtypes")
144   public static BuilderSwap<?,?> findSwapFromBuilderClass(Class<?> builderClass, Visibility cVis, Visibility mVis) {
145      ClassInfo bci = getClassInfo(builderClass);
146      if (bci.isNotPublic())
147         return null;
148
149//    Class<?> pojoClass = getClassInfo(Builder.class).getParameterType(0, builderClass);
150      Class<?> pojoClass = getClassInfo(builderClass).getParameterType(0, Builder.class);
151
152      MethodInfo createPojoMethod, createBuilderMethod;
153      ConstructorInfo pojoConstructor;
154      ConstructorInfo builderConstructor;
155
156      createPojoMethod = bci.getBuilderBuildMethod();
157      if (createPojoMethod != null)
158         pojoClass = createPojoMethod.getReturnType().inner();
159
160      if (pojoClass == null)
161         return null;
162
163      ClassInfo pci = getClassInfo(pojoClass);
164
165      pojoConstructor = pci.getConstructor(cVis, builderClass);
166      if (pojoConstructor == null)
167         return null;
168
169      builderConstructor = bci.getNoArgConstructor(cVis);
170      createBuilderMethod = pci.getBuilderCreateMethod();
171      if (builderConstructor == null && createBuilderMethod == null)
172         return null;
173
174      return new BuilderSwap(pojoClass, builderClass, pojoConstructor.inner(), builderConstructor == null ? null : builderConstructor.inner(), createBuilderMethod, createPojoMethod);
175   }
176
177
178   /**
179    * Creates a BuilderSwap from the specified POJO class if it has one.
180    *
181    * @param pojoClass The POJO class to check.
182    * @param cVis Minimum constructor visibility.
183    * @param mVis Minimum method visibility.
184    * @return A new swap instance, or <jk>null</jk> if class didn't have a builder class.
185    */
186   @SuppressWarnings("rawtypes")
187   public static BuilderSwap<?,?> findSwapFromPojoClass(Class<?> pojoClass, Visibility cVis, Visibility mVis) {
188      Class<?> builderClass = null;
189      MethodInfo pojoCreateMethod, builderCreateMethod;
190      ConstructorInfo pojoConstructor = null;
191      ConstructorInfo builderConstructor;
192
193      org.apache.juneau.annotation.Builder b = pojoClass.getAnnotation(org.apache.juneau.annotation.Builder.class);
194
195      if (b != null && b.value() != Null.class)
196         builderClass = b.value();
197
198      ClassInfo pci = getClassInfo(pojoClass);
199
200      builderCreateMethod = pci.getBuilderCreateMethod();
201
202      if (builderClass == null && builderCreateMethod != null)
203         builderClass = builderCreateMethod.getReturnType().inner();
204
205      if (builderClass == null) {
206         for (ConstructorInfo cc : pci.getPublicConstructors()) {
207            if (cc.isVisible(cVis) && cc.hasNumParams(1)) {
208               ClassInfo pt = cc.getParamType(0);
209               if (pt.isChildOf(Builder.class)) {
210                  pojoConstructor = cc;
211                  builderClass = pt.inner();
212               }
213            }
214         }
215      }
216
217      if (builderClass == null)
218         return null;
219
220      ClassInfo bci = getClassInfo(builderClass);
221      builderConstructor = bci.getNoArgConstructor(cVis);
222      if (builderConstructor == null && builderCreateMethod == null)
223         return null;
224
225      pojoCreateMethod = bci.getBuilderBuildMethod();
226      if (pojoConstructor == null)
227         pojoConstructor = pci.getConstructor(cVis, builderClass);
228
229      if (pojoConstructor == null && pojoCreateMethod == null)
230         return null;
231
232      return new BuilderSwap(pojoClass, builderClass, pojoConstructor == null ? null : pojoConstructor.inner(), builderConstructor == null ? null : builderConstructor.inner(), builderCreateMethod, pojoCreateMethod);
233   }
234}