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

org.apache.juneau.internal.TransformCache Maven / Gradle / Ivy

There is a newer version: 9.0.1
Show newest version
// ***************************************************************************************************************************
// * 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.internal;

import java.util.concurrent.*;

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

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.
 */
public class TransformCache {
	private static final ConcurrentHashMap,Map,Transform>> CACHE = new ConcurrentHashMap<>();

	/**
	 * Represents a non-existent transform.
	 */
	public static final Transform NULL = new Transform() {
		@Override
		public Object transform(Object outer, Object in) {
			return null;
		}
	};

	// Special cases.
	static {

		// TimeZone doesn't follow any standard conventions.
		add(String.class, TimeZone.class,
			new Transform() {
				@Override public TimeZone transform(Object outer, String in) {
					return TimeZone.getTimeZone(in);
				}
			}
		);
		add(TimeZone.class, String.class,
			new Transform() {
				@Override public String transform(Object outer, TimeZone in) {
					return in.getID();
				}
			}
		);

		// Locale(String) doesn't work on strings like "ja_JP".
		add(String.class, Locale.class,
			new Transform() {
				@Override
				public Locale transform(Object outer, String in) {
					return Locale.forLanguageTag(in.replace('_', '-'));
				}
			}
		);

		// String-to-Boolean transform should allow for "null" keyword.
		add(String.class, Boolean.class,
			new Transform() {
				@Override
				public Boolean transform(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, Transform t) {
		Map,Transform> 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 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  Transform get(final Class ic, final Class oc) {

		if (ic == null || oc == null)
			return null;

		Map,Transform> m = CACHE.get(oc);
		if (m == null) {
			m = new ConcurrentHashMap<>();
			CACHE.putIfAbsent(oc, m);
			m = CACHE.get(oc);
		}

		Transform t = m.get(ic);
		if (t != null)
			return t == NULL ? null : t;

		for (Iterator> i = ClassUtils.getParentClasses(ic, false, true); i.hasNext(); ) {
			Class pic = i.next();
			t = m.get(pic);
			if (t != null) {
				m.put(pic, t);
				return t == NULL ? null : t;
			}
		}

		if (ic == oc) {
			t = new Transform() {
				@Override public O transform(Object outer, I in) {
					return (O)in;
				}
			};
		} else if (ic == String.class) {
			final Class oc2 = hasPrimitiveWrapper(oc) ? getPrimitiveWrapper(oc) : oc;
			if (oc2.isEnum()) {
				t = new Transform() {
					@Override
					public O transform(Object outer, String in) {
						return (O)Enum.valueOf((Class)oc2, in);
					}
				};
			} else {
				final Method fromStringMethod = findPublicFromStringMethod(oc2);
				if (fromStringMethod != null) {
					t = new Transform() {
						@Override
						public O transform(Object outer, String in) {
							try {
								return (O)fromStringMethod.invoke(null, in);
							} catch (Exception e) {
								throw new RuntimeException(e);
							}
						}
					};
				}
			}
		}

		if (t == null) {
			Method createMethod = findPublicStaticCreateMethod(oc, ic, "create");
			if (createMethod == null)
				createMethod = findPublicStaticCreateMethod(oc, ic, "from" + ic.getSimpleName());
			if (createMethod != null) {
				final Method cm = createMethod;
				t = new Transform() {
					@Override
					public O transform(Object context, I in) {
						try {
							return (O)cm.invoke(null, in);
						} catch (Exception e) {
							throw new RuntimeException(e);
						}
					}
				};
			} else {
				final Constructor c = findPublicConstructor(oc, ic);
				final boolean isMemberClass = oc.isMemberClass() && ! isStatic(oc);
				if (c != null && ! isDeprecated(c)) {
					t = new Transform() {
						@Override
						public O transform(Object outer, I in) {
							try {
								if (isMemberClass)
									return (O)c.newInstance(outer, in);
								return (O)c.newInstance(in);
							} catch (Exception e) {
								throw new RuntimeException(e);
							}
						}
					};
				}

			}
		}

		if (t == null) {
			for (Method m2 : getAllMethods(ic, false)) {
				if (isAll(m2, PUBLIC, NOT_STATIC, HAS_NO_ARGS, NOT_DEPRECATED) && m2.getName().startsWith("to") && m2.getReturnType() == oc) {
					final Method m3 = m2;
					t = new Transform() {
						@Override
						public O transform(Object outer, I in) {
							try {
								return (O)m3.invoke(in);
							} catch (Exception e) {
								throw new RuntimeException(e);
							}
						}
					};
					break;
				}
			}
		}
		if (t == null)
			t = NULL;

		m.put(ic, t);

		return t == NULL ? null : t;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy