001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.swap;
018
019import static org.apache.juneau.internal.ClassUtils.*;
020
021import java.lang.reflect.*;
022import java.util.*;
023
024import org.apache.juneau.*;
025import org.apache.juneau.annotation.*;
026import org.apache.juneau.parser.*;
027import org.apache.juneau.reflect.*;
028import org.apache.juneau.serializer.*;
029
030/**
031 * Specialized {@link ObjectSwap} for {@link Surrogate} classes.
032 *
033 * <h5 class='section'>See Also:</h5><ul>
034 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/SwapBasics">Swap Basics</a>
035 * </ul>
036 *
037 * @param <T> The class type that this transform applies to.
038 * @param <F> The transformed class type.
039 */
040public class SurrogateSwap<T,F> extends ObjectSwap<T,F> {
041
042   private Constructor<F> constructor;   // public F(T t);
043   private Method unswapMethod;        // public T build();
044
045   /**
046    * Constructor.
047    *
048    * @param forClass The normal class.
049    * @param constructor The constructor on the surrogate class that takes the normal class as a parameter.
050    * @param unswapMethod The static method that converts surrogate objects into normal objects.
051    */
052   protected SurrogateSwap(Class<T> forClass, Constructor<F> constructor, Method unswapMethod) {
053      super(forClass, constructor.getDeclaringClass());
054      this.constructor = constructor;
055      this.unswapMethod = unswapMethod;
056   }
057
058   /**
059    * Given the specified surrogate class, return the list of object swaps.
060    *
061    * <p>
062    * A transform is returned for each public 1-arg constructor found.
063    * Returns an empty list if no public 1-arg constructors are found.
064    *
065    * @param c The surrogate class.
066    * @param bc The bean context to use for looking up annotations.
067    * @return The list of object swaps that apply to this class.
068    */
069   @SuppressWarnings({"unchecked", "rawtypes"})
070   public static List<SurrogateSwap<?,?>> findObjectSwaps(Class<?> c, BeanContext bc) {
071      List<SurrogateSwap<?,?>> l = new LinkedList<>();
072      ClassInfo ci = ClassInfo.of(c);
073      ci.forEachPublicConstructor(
074         x -> x.hasNoAnnotation(bc, BeanIgnore.class) && x.hasNumParams(1) && x.isPublic(),
075         x -> {
076            Class<?> pt = x.getRawParamType(0);
077            if (! pt.equals(c.getDeclaringClass())) {
078               // Find the unswap method if there is one.
079               MethodInfo mi = ci.getPublicMethod(y -> y.hasReturnType(pt));
080               Method unswapMethod = mi != null ? mi.inner() : null;
081               l.add(new SurrogateSwap(pt, x.inner(), unswapMethod));
082            }
083         }
084      );
085      return l;
086   }
087
088   @Override /* ObjectSwap */
089   public F swap(BeanSession session, T o) throws SerializeException {
090      try {
091         return constructor.newInstance(o);
092      } catch (Exception e) {
093         throw new SerializeException(e);
094      }
095   }
096
097   @Override /* ObjectSwap */
098   @SuppressWarnings("unchecked")
099   public T unswap(BeanSession session, F f, ClassMeta<?> hint) throws ParseException {
100      if (unswapMethod == null)
101         throw new ParseException("unswap() method not implement on surrogate class ''{1}'': {0}", className(f), getNormalClass().getFullName());
102      try {
103         return (T)unswapMethod.invoke(f);
104      } catch (Exception e) {
105         throw new ParseException(e);
106      }
107   }
108}