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.*;
021
022/**
023 * Specialized transform for builder classes.
024 * 
025 * <h5 class='section'>See Also:</h5>
026 * <ul>
027 *    <li class='link'><a class="doclink" href="../../../../overview-summary.html#juneau-marshall.PojoBuilders">Overview &gt; juneau-marshall &gt; POJO Builders</a>
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 Method createBuilderMethod;          // Builder create();
041   private final Method 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, Method createBuilderMethod, Method 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 Exception
105    */
106   public B create(BeanSession session, ClassMeta<?> hint) throws Exception {
107      if (createBuilderMethod != null)
108         return (B)createBuilderMethod.invoke(null);
109      return builderConstructor.newInstance();
110   }
111   
112   /**
113    * Creates a new POJO from the specified builder.
114    * 
115    * @param session The current bean session.
116    * @param builder The POJO builder.
117    * @param hint A hint about the class type.
118    * @return A new POJO.
119    * @throws Exception
120    */
121   public T build(BeanSession session, B builder, ClassMeta<?> hint) throws Exception {
122      if (createPojoMethod != null)
123         return (T)createPojoMethod.invoke(builder);
124      return pojoConstructor.newInstance(builder);
125   }
126   
127   /**
128    * Creates a BuilderSwap from the specified builder class if it qualifies as one.
129    * 
130    * @param builderClass The potential builder class.
131    * @param cVis Minimum constructor visibility.
132    * @param mVis Minimum method visibility.
133    * @return A new swap instance, or <jk>null</jk> if class wasn't a builder class.
134    */
135   @SuppressWarnings("rawtypes")
136   public static BuilderSwap<?,?> findSwapFromBuilderClass(Class<?> builderClass, Visibility cVis, Visibility mVis) {
137      if (! isPublic(builderClass))
138         return null;
139      
140      Class<?> pojoClass = resolveParameterType(Builder.class, 0, builderClass);
141
142      Method createPojoMethod, createBuilderMethod;
143      Constructor<?> pojoConstructor, builderConstructor;
144      
145      createPojoMethod = findCreatePojoMethod(builderClass);
146      if (createPojoMethod != null)
147         pojoClass = createPojoMethod.getReturnType();
148      
149      if (pojoClass == null)
150         return null;
151
152      pojoConstructor = findConstructor(pojoClass, cVis, false, builderClass);
153      if (pojoConstructor == null)
154         return null;
155      
156      builderConstructor = findNoArgConstructor(builderClass, cVis);
157      createBuilderMethod = findBuilderCreateMethod(pojoClass);
158      if (builderConstructor == null && createBuilderMethod == null)
159         return null;
160      
161      return new BuilderSwap(pojoClass, builderClass, pojoConstructor, builderConstructor, createBuilderMethod, createPojoMethod);
162   }
163   
164
165   /**
166    * Creates a BuilderSwap from the specified POJO class if it has one.
167    * 
168    * @param pojoClass The POJO class to check.
169    * @param cVis Minimum constructor visibility.
170    * @param mVis Minimum method visibility.
171    * @return A new swap instance, or <jk>null</jk> if class didn't have a builder class.
172    */
173   @SuppressWarnings("rawtypes")
174   public static BuilderSwap<?,?> findSwapFromPojoClass(Class<?> pojoClass, Visibility cVis, Visibility mVis) {
175      Class<?> builderClass = null;
176      Method pojoCreateMethod, builderCreateMethod;
177      Constructor<?> pojoConstructor = null, builderConstructor;
178
179      org.apache.juneau.annotation.Builder b = pojoClass.getAnnotation(org.apache.juneau.annotation.Builder.class);
180      
181      if (b != null && b.value() != Null.class) 
182         builderClass = b.value();
183      
184      builderCreateMethod = findBuilderCreateMethod(pojoClass);
185
186      if (builderClass == null && builderCreateMethod != null) 
187         builderClass = builderCreateMethod.getReturnType();
188      
189      if (builderClass == null) {
190         for (Constructor cc : pojoClass.getConstructors()) {
191            if (cVis.isVisible(cc)) {
192               Class<?>[] pt = cc.getParameterTypes();
193               if (pt.length == 1 && isParentClass(Builder.class, pt[0])) {
194                  pojoConstructor = cc;
195                  builderClass = pt[0];
196               }
197            }
198         }
199      }
200      
201      if (builderClass == null)
202         return null;
203      
204      builderConstructor = findNoArgConstructor(builderClass, cVis);
205      if (builderConstructor == null && builderCreateMethod == null)
206         return null;
207
208      pojoCreateMethod = findCreatePojoMethod(builderClass);
209      if (pojoConstructor == null)
210         pojoConstructor = findConstructor(pojoClass, cVis, false, builderClass);
211      
212      if (pojoConstructor == null && pojoCreateMethod == null)
213         return null;
214
215      return new BuilderSwap(pojoClass, builderClass, pojoConstructor, builderConstructor, builderCreateMethod, pojoCreateMethod);
216   }
217
218   private static Method findBuilderCreateMethod(Class<?> pojoClass) {
219      for (Method m : pojoClass.getDeclaredMethods()) 
220         if (isPublic(m) && isStatic(m) && m.getName().equals("create") && m.getReturnType() != Void.class)
221            return m;
222      return null;
223   }
224      
225   private static Method findCreatePojoMethod(Class<?> builderClass) {
226      for (Method m : builderClass.getDeclaredMethods()) 
227         if ("build".equals(m.getName()) && ! (isStatic(m) || m.getReturnType() == Void.class)) 
228            return m;
229      return null;
230   }
231}