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.marshall.swap.spi; 018 019import static org.apache.juneau.commons.reflect.ReflectionUtils.*; 020import static org.apache.juneau.commons.utils.Shorts.*; 021 022import java.lang.reflect.*; 023import java.util.*; 024 025import org.apache.juneau.commons.bean.*; 026import org.apache.juneau.commons.reflect.*; 027import org.apache.juneau.marshall.*; 028import org.apache.juneau.marshall.parser.*; 029import org.apache.juneau.marshall.serializer.*; 030 031/** 032 * Specialized {@link ObjectSwap} for {@link Surrogate} classes. 033 * 034 * <h5 class='section'>See Also:</h5><ul> 035 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/Swaps">Swap</a> 036 * </ul> 037 * 038 * @param <T> The class type that this transform applies to. 039 * @param <F> The transformed class type. 040 */ 041@SuppressWarnings({ 042 "unchecked" // unswap() casts the result of the reflective unswap-method invocation to T; the method is selected to return the surrogate's normal class 043}) 044public class SurrogateSwap<T,F> extends ObjectSwap<T,F> { 045 046 /** 047 * Given the specified surrogate class, return the list of object swaps. 048 * 049 * <p> 050 * A transform is returned for each public 1-arg constructor found. 051 * Returns an empty list if no public 1-arg constructors are found. 052 * 053 * @param c The surrogate class. Must not be <jk>null</jk>. 054 * @param bc The bean context to use for looking up annotations. Must not be <jk>null</jk>. 055 * @return The list of object swaps that apply to this class. Never <jk>null</jk>. 056 */ 057 @SuppressWarnings({ 058 "java:S1452", // Wildcard required - List<SurrogateSwap<?,?>> for multiple constructor-based swaps 059 "rawtypes" // Raw types necessary for generic type handling 060 }) 061 public static List<SurrogateSwap<?,?>> findObjectSwaps(Class<?> c, MarshallingContext bc) { 062 List<SurrogateSwap<?,?>> l = ll(); 063 var ci = info(c); 064 ci.getPublicConstructors().stream().filter(x -> ! bc.getAnnotationProvider().has(BeanIgnore.class, x) && x.hasNumParameters(1) && x.isPublic()).forEach(x -> { 065 var pt = x.getParameter(0).getParameterType().inner(); 066 if (! pt.equals(c.getDeclaringClass())) { 067 // Find the unswap method if there is one. 068 Method unswapMethod = ci.getPublicMethod(y -> y.hasReturnType(pt)).map(MethodInfo::inner).orElse(null); 069 l.add(new SurrogateSwap(pt, x.inner(), unswapMethod)); 070 } 071 }); 072 return l; 073 } 074 075 private final Constructor<F> constructor; 076 077 private final Method unswapMethod; 078 079 /** 080 * Constructor. 081 * 082 * @param forClass The normal class. 083 * @param constructor The constructor on the surrogate class that takes the normal class as a parameter. Must not be <jk>null</jk>. 084 * @param unswapMethod 085 * The method on the surrogate class that converts surrogate objects back into normal objects. 086 * Can be <jk>null</jk> if the surrogate is serialize-only (calling {@link #unswap(MarshallingSession, Object, ClassMeta)} then throws a {@link ParseException}). 087 */ 088 protected SurrogateSwap(Class<T> forClass, Constructor<F> constructor, Method unswapMethod) { 089 super(forClass, constructor.getDeclaringClass()); 090 this.constructor = constructor; 091 this.unswapMethod = unswapMethod; 092 } 093 094 @Override /* Overridden from ObjectSwap */ 095 public F swap(MarshallingSession session, T o) throws SerializeException { 096 try { 097 return constructor.newInstance(o); 098 } catch (Exception e) { 099 throw new SerializeException(e); 100 } 101 } 102 103 @Override /* Overridden from ObjectSwap */ 104 public T unswap(MarshallingSession session, F f, ClassMeta<?> hint) throws ParseException { 105 if (unswapMethod == null) 106 throw new ParseException("unswap() method not implement on surrogate class '%2$s': %1$s", cn(f), getNormalClass().getNameFull()); 107 try { 108 return (T)unswapMethod.invoke(f); 109 } catch (Exception e) { 110 throw new ParseException(e); 111 } 112 } 113}