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

org.apache.juneau.swap.ObjectSwap Maven / Gradle / Ivy

// ***************************************************************************************************************************
// * 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.swap;

import static org.apache.juneau.internal.ClassUtils.*;

import java.util.*;

import org.apache.juneau.*;
import org.apache.juneau.annotation.*;
import org.apache.juneau.parser.*;
import org.apache.juneau.reflect.*;
import org.apache.juneau.serializer.*;

/**
 * Used to swap out non-serializable objects with serializable replacements during serialization, and vis-versa during
 * parsing.
 *
 * 
Description
* *

* ObjectSwaps are used to extend the functionality of the serializers and parsers to be able to handle * objects that aren't automatically handled by the serializers or parsers. *
For example, JSON does not have a standard representation for rendering dates. * By defining a special {@code Date} swap and associating it with a serializer and parser, you can convert a * {@code Date} object to a {@code String} during serialization, and convert that {@code String} object back into a * {@code Date} object during parsing. * *

* Swaps MUST declare a public no-arg constructor so that the bean context can instantiate them. * *

* ObjectSwaps are associated with serializers and parsers through the following: *

    *
  • {@link Swap @Swap} *
  • {@link org.apache.juneau.BeanContext.Builder#swaps(Class...)} *
* *

* ObjectSwaps have two parameters: *

    *
  1. {@code } - The normal representation of an object. *
  2. {@code } - The swapped representation of an object. *
*
{@link Serializer Serializers} use swaps to convert objects of type T into objects of type S, and on calls to * {@link BeanMap#get(Object)}. *
{@link Parser Parsers} use swaps to convert objects of type S into objects of type T, and on calls to * {@link BeanMap#put(String,Object)}. * *
Swap Class Type {@code }
* *

* For normal serialization, the swapped object representation of an object must be an object type that the serializers can natively convert to * JSON (or language-specific equivalent). *
The list of valid transformed types are as follows... *

    *
  • * {@link String} *
  • * {@link Number} *
  • * {@link Boolean} *
  • * {@link Collection} containing anything on this list. *
  • * {@link Map} containing anything on this list. *
  • * A java bean with properties of anything on this list. *
  • * An array of anything on this list. *
* *

* For OpenAPI serialization, the valid swapped types also include byte[] and Calendar. * *

Normal Class Type {@code }
* *

* The normal object representation of an object. * *

See Also:
* * @param The normal form of the class. * @param The swapped form of the class. */ @SuppressWarnings({"unchecked","rawtypes"}) public abstract class ObjectSwap { /** * Represents a non-existent object swap. */ public static final ObjectSwap NULL = new ObjectSwap((Class)null, (Class)null) {}; private final Class normalClass; private final Class swapClass; private final ClassInfo normalClassInfo, swapClassInfo; private ClassMeta swapClassMeta; // Unfortunately these cannot be made final because we want to allow for ObjectSwaps with no-arg constructors // which simplifies declarations. private MediaType[] forMediaTypes; private String template; /** * Constructor. */ protected ObjectSwap() { ClassInfo ci = ClassInfo.of(this.getClass()); normalClass = (Class)ci.getParameterType(0, ObjectSwap.class); swapClass = ci.getParameterType(1, ObjectSwap.class); normalClassInfo = ClassInfo.of(normalClass); swapClassInfo = ClassInfo.of(swapClass); forMediaTypes = forMediaTypes(); template = withTemplate(); } /** * Constructor for when the normal and transformed classes are already known. * * @param normalClass The normal class (cannot be serialized). * @param swapClass The transformed class (serializable). */ protected ObjectSwap(Class normalClass, Class swapClass) { this.normalClass = normalClass; this.swapClass = swapClass; normalClassInfo = ClassInfo.of(normalClass); swapClassInfo = ClassInfo.of(swapClass); this.forMediaTypes = forMediaTypes(); this.template = withTemplate(); } /** * Returns the media types that this swap is applicable to. * *

* This method can be overridden to programmatically specify what media types it applies to. * *

* This method is the programmatic equivalent to the {@link Swap#mediaTypes() @Swap(mediaTypes)} annotation. * *

See Also:
* @return The media types that this swap is applicable to, or null if it's applicable for all media types. */ public MediaType[] forMediaTypes() { return null; } /** * Returns additional context information for this swap. * *

* Typically this is going to be used to specify a template name, such as a FreeMarker template file name. * *

* This method can be overridden to programmatically specify a template value. * *

* This method is the programmatic equivalent to the {@link Swap#template() @Swap(template)} annotation. * *

See Also:
* * @return Additional context information, or null if not specified. */ public String withTemplate() { return null; } /** * Sets the media types that this swap is associated with. * *
See Also:
* * @param mediaTypes The media types that this swap is associated with. * @return This object. */ public ObjectSwap forMediaTypes(MediaType[] mediaTypes) { this.forMediaTypes = mediaTypes; return this; } /** * Sets the template string on this swap. * *
See Also:
* * @param template The template string on this swap. * @return This object. */ public ObjectSwap withTemplate(String template) { this.template = template; return this; } /** * Returns a number indicating how well this swap matches the specified session. * *

* Uses the {@link MediaType#match(MediaType, boolean)} method algorithm to produce a number whereby a * larger value indicates a "better match". * The idea being that if multiple swaps are associated with a given object, we want to pick the best one. * *

* For example, if the session media type is "text/json", then the match values are shown below: * *

    *
  • "text/json" = 100,000 *
  • "*/json" = 5,100 *
  • "*/*" = 5,000 *
  • No media types specified on swap = 1 *
  • "text/xml" = 0 *
* * @param session The bean session. * @return Zero if swap doesn't match the session, or a positive number if it does. */ public int match(BeanSession session) { if (forMediaTypes == null) return 1; int i = 0; MediaType mt = session.getMediaType(); if (mt == null) return 0; if (forMediaTypes != null) for (MediaType mt2 : forMediaTypes) i = Math.max(i, mt2.match(mt, false)); return i; } /** * If this transform is to be used to serialize non-serializable objects, it must implement this method. * *

* The object must be converted into one of the following serializable types: *

    *
  • * {@link String} *
  • * {@link Number} *
  • * {@link Boolean} *
  • * {@link Collection} containing anything on this list. *
  • * {@link Map} containing anything on this list. *
  • * A java bean with properties of anything on this list. *
  • * An array of anything on this list. *
* * @param session * The bean session to use to get the class meta. * This is always going to be the same bean context that created this swap. * @param o The object to be transformed. * @return The transformed object. * @throws Exception If a problem occurred trying to convert the output. */ public S swap(BeanSession session, T o) throws Exception { return swap(session, o, template); } /** * Same as {@link #swap(BeanSession, Object)}, but can be used if your swap has a template associated with it. * * @param session * The bean session to use to get the class meta. * This is always going to be the same bean context that created this swap. * @param o The object to be transformed. * @param template * The template string associated with this swap. * @return The transformed object. * @throws Exception If a problem occurred trying to convert the output. */ public S swap(BeanSession session, T o, String template) throws Exception { throw new SerializeException("Swap method not implemented on ObjectSwap ''{0}''", className(this)); } /** * If this transform is to be used to reconstitute objects that aren't true Java beans, it must implement this method. * * @param session * The bean session to use to get the class meta. * This is always going to be the same bean context that created this swap. * @param f The transformed object. * @param hint * If possible, the parser will try to tell you the object type being created. * For example, on a serialized date, this may tell you that the object being created must be of type * {@code GregorianCalendar}. *
This may be null if the parser cannot make this determination. * @return The narrowed object. * @throws Exception If this method is not implemented. */ public T unswap(BeanSession session, S f, ClassMeta hint) throws Exception { return unswap(session, f, hint, template); } /** * Same as {@link #unswap(BeanSession, Object, ClassMeta)}, but can be used if your swap has a template associated with it. * * @param session * The bean session to use to get the class meta. * This is always going to be the same bean context that created this swap. * @param f The transformed object. * @param hint * If possible, the parser will try to tell you the object type being created. * For example, on a serialized date, this may tell you that the object being created must be of type * {@code GregorianCalendar}. *
This may be null if the parser cannot make this determination. * @param template * The template string associated with this swap. * @return The transformed object. * @throws Exception If a problem occurred trying to convert the output. */ public T unswap(BeanSession session, S f, ClassMeta hint, String template) throws Exception { throw new ParseException("Unswap method not implemented on ObjectSwap ''{0}''", className(this)); } /** * Returns the T class, the normalized form of the class. * * @return The normal form of this class. */ public ClassInfo getNormalClass() { return normalClassInfo; } /** * Returns the G class, the generalized form of the class. * *

* Subclasses must override this method if the generalized class is {@code Object}, meaning it can produce multiple * generalized forms. * * @return The transformed form of this class. */ public ClassInfo getSwapClass() { return swapClassInfo; } /** * 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 getSwapClassMeta(BeanSession session) { if (swapClassMeta == null) swapClassMeta = session.getClassMeta(swapClass); return swapClassMeta; } /** * Checks if the specified object is an instance of the normal class defined on this swap. * * @param o The object to check. * @return * true if the specified object is a subclass of the normal class defined on this transform. * null always return false. */ public boolean isNormalObject(Object o) { if (o == null) return false; return normalClassInfo.isParentOf(o.getClass()); } /** * Checks if the specified object is an instance of the swap class defined on this swap. * * @param o The object to check. * @return * true if the specified object is a subclass of the transformed class defined on this transform. * null always return false. */ public boolean isSwappedObject(Object o) { if (o == null) return false; return swapClassInfo.isParentOf(o.getClass()); } //----------------------------------------------------------------------------------------------------------------- // Overridden methods //----------------------------------------------------------------------------------------------------------------- @Override /* Object */ public String toString() { return getClass().getSimpleName() + '<' + getNormalClass().getSimpleName() + "," + getSwapClass().getSimpleName() + '>'; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy