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

org.apache.juneau.reflect.Mutaters 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.reflect;

import java.util.concurrent.*;

import static org.apache.juneau.common.internal.StringUtils.*;
import static org.apache.juneau.common.internal.ThrowableUtils.*;

import java.lang.reflect.*;
import java.util.*;

/**
 * Cache of object that convert POJOs to and from common types such as strings, readers, and input streams.
 *
 * 
See Also:
    *
*/ public class Mutaters { private static final ConcurrentHashMap,Map,Mutater>> CACHE = new ConcurrentHashMap<>(); /** * Represents a non-existent transform. */ public static final Mutater NULL = new Mutater<>() { @Override public Object mutate(Object outer, Object in) { return null; } }; // Special cases. static { // TimeZone doesn't follow any standard conventions. add(String.class, TimeZone.class, new Mutater() { @Override public TimeZone mutate(Object outer, String in) { return TimeZone.getTimeZone(in); } } ); add(TimeZone.class, String.class, new Mutater() { @Override public String mutate(Object outer, TimeZone in) { return in.getID(); } } ); // Locale(String) doesn't work on strings like "ja_JP". add(String.class, Locale.class, new Mutater() { @Override public Locale mutate(Object outer, String in) { return Locale.forLanguageTag(in.replace('_', '-')); } } ); // String-to-Boolean transform should allow for "null" keyword. add(String.class, Boolean.class, new Mutater() { @Override public Boolean mutate(Object outer, String in) { if (in == null || "null".equals(in) || in.isEmpty()) return null; return Boolean.valueOf(in); } } ); } /** * Adds a transform for the specified input/output types. * * @param ic The input type. * @param oc The output type. * @param t The transform for converting the input to the output. */ public static synchronized void add(Class ic, Class oc, Mutater t) { Map,Mutater> m = CACHE.get(oc); if (m == null) { m = new ConcurrentHashMap<>(); CACHE.put(oc, m); } m.put(ic, t); } /** * Returns the transform for converting the specified input type to the specified output type. * * @param The input type. * @param The output type. * @param ic The input type. * @param oc The output type. * @return The transform for performing the conversion, or null if the conversion cannot be made. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public static Mutater get(Class ic, Class oc) { if (ic == null || oc == null) return null; Map,Mutater> m = CACHE.get(oc); if (m == null) { m = new ConcurrentHashMap<>(); CACHE.putIfAbsent(oc, m); m = CACHE.get(oc); } Mutater t = m.get(ic); if (t == null) { t = find(ic, oc, m); m.put(ic, t); } return t == NULL ? null : t; } /** * Returns the transform for converting the specified input type to the specified output type. * * @param The input type. * @param The output type. * @param ic The input type. * @param oc The output type. * @return The transform for performing the conversion, or null if the conversion cannot be made. */ public static boolean hasMutate(Class ic, Class oc) { return get(ic, oc) != NULL; } @SuppressWarnings({"unchecked","rawtypes"}) private static Mutater find(Class ic, Class oc, Map,Mutater> m) { if (ic == oc) { return new Mutater() { @Override public Object mutate(Object outer, Object in) { return in; } }; } ClassInfo ici = ClassInfo.of(ic), oci = ClassInfo.of(oc); ClassInfo pic = ici.getAnyParent(x -> m.get(x.inner()) != null); if (pic != null) return m.get(pic.inner()); if (ic == String.class) { Class oc2 = oci.hasPrimitiveWrapper() ? oci.getPrimitiveWrapper() : oc; ClassInfo oc2i = ClassInfo.of(oc2); final MethodInfo createMethod = oc2i.getPublicMethod( x -> x.isStatic() && x.isNotDeprecated() && x.hasReturnType(oc2) && x.hasParamTypes(ic) && (x.hasName("forName") || isStaticCreateMethodName(x, ic)) ); if (oc2.isEnum() && createMethod == null) { return new Mutater() { @Override public Object mutate(Object outer, String in) { return Enum.valueOf((Class)oc2, in); } }; } if (createMethod != null) { return new Mutater() { @Override public Object mutate(Object outer, String in) { try { return createMethod.invoke(null, in); } catch (Exception e) { throw asRuntimeException(e); } } }; } } else { MethodInfo createMethod = oci.getPublicMethod( x -> x.isStatic() && x.isNotDeprecated() && x.hasReturnType(oc) && x.hasParamTypes(ic) && isStaticCreateMethodName(x, ic) ); if (createMethod != null) { Method cm = createMethod.inner(); return new Mutater() { @Override public Object mutate(Object context, Object in) { try { return cm.invoke(null, in); } catch (Exception e) { throw asRuntimeException(e); } } }; } } ConstructorInfo c = oci.getPublicConstructor(x -> x.hasParamTypes(ic)); if (c != null && c.isNotDeprecated()) { boolean isMemberClass = oci.isNonStaticMemberClass(); return new Mutater() { @Override public Object mutate(Object outer, Object in) { try { if (isMemberClass) return c.invoke(outer, in); return c.invoke(in); } catch (Exception e) { throw asRuntimeException(e); } } }; } MethodInfo toXMethod = findToXMethod(ici, oci); if (toXMethod != null) { return new Mutater() { @Override public Object mutate(Object outer, Object in) { try { return toXMethod.invoke(in); } catch (Exception e) { throw asRuntimeException(e); } } }; } return NULL; } private static boolean isStaticCreateMethodName(MethodInfo mi, Class ic) { String n = mi.getSimpleName(), cn = ic.getSimpleName(); return isOneOf(n, "create","from","fromValue","parse","valueOf") || (n.startsWith("from") && n.substring(4).equals(cn)) || (n.startsWith("for") && n.substring(3).equals(cn)) || (n.startsWith("parse") && n.substring(5).equals(cn)); } /** * Constructs a new instance of the specified class from the specified string. * *

* Class must be one of the following: *

    *
  • Have a public constructor that takes in a single String argument. *
  • Have a static fromString(String) (or related) method. *
  • Be an enum. *
* * @param The class type. * @param c The class type. * @param s The string to create the instance from. * @return A new object instance, or null if a method for converting the string to an object could not be found. */ public static T fromString(Class c, String s) { Mutater t = get(String.class, c); return t == null ? null : t.mutate(s); } /** * Converts an object to a string. * *

* Normally, this is just going to call toString() on the object. * However, the {@link Locale} and {@link TimeZone} objects are treated special so that the returned value * works with the {@link #fromString(Class, String)} method. * * @param o The object to convert to a string. * @return The stringified object, or null if the object was null. */ @SuppressWarnings({ "unchecked" }) public static String toString(Object o) { if (o == null) return null; Mutater t = (Mutater)get(o.getClass(), String.class); return t == null ? o.toString() : t.mutate(o); } private static MethodInfo findToXMethod(ClassInfo ic, ClassInfo oc) { String tn = oc.getReadableName(); return ic.getPublicMethod( x -> x.isNotStatic() && x.hasNoParams() && x.getSimpleName().startsWith("to") && x.getSimpleName().substring(2).equalsIgnoreCase(tn) ); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy