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

org.ldp4j.application.sdk.ObjectUtil Maven / Gradle / Ivy

/**
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   This file is part of the LDP4j Project:
 *     http://www.ldp4j.org/
 *
 *   Center for Open Middleware
 *     http://www.centeropenmiddleware.com/
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Copyright (C) 2014 Center for Open Middleware.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Licensed 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.
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 *   Artifact    : org.ldp4j.framework:ldp4j-application-engine-sdk:0.2.0
 *   Bundle      : ldp4j-application-engine-sdk-0.2.0.jar
 * #-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=#
 */
package org.ldp4j.application.sdk;

import static com.google.common.base.Preconditions.checkNotNull;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.Set;

import org.ldp4j.application.ext.ObjectTransformationException;
import org.ldp4j.application.sdk.internal.EnumObjectFactory;
import org.ldp4j.application.sdk.internal.PrimitiveObjectFactory;
import org.ldp4j.application.sdk.internal.ReflectionObjectFactory;
import org.ldp4j.application.sdk.spi.ObjectFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

final class ObjectUtil {

	/**
	 * An object factory which fails to parse/format values of a specific type.
	 */
	private static final class NullObjectFactory implements ObjectFactory {

		private final Class targetClass;

		private NullObjectFactory(Class valueClass) {
			this.targetClass = valueClass;
		}

		private ObjectTransformationException failure() {
			return new ObjectTransformationException("Unsupported value type '"+prettyPrint(targetClass())+"'",null,targetClass());
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public Class targetClass() {
			return this.targetClass;
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public T fromString(String rawValue) {
			throw failure();
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public String toString(T value) {
			throw failure();
		}

	}

	private static final Logger LOGGER=LoggerFactory.getLogger(ObjectUtil.class);

	private static final Map,ObjectFactory> FACTORY_CACHE=Maps.newIdentityHashMap();
	private static final Set> PRELOADED_FACTORIES=Sets.newIdentityHashSet();

	private ObjectUtil() {
	}

	private static String prettyPrint(Class clazz) {
		return clazz.getCanonicalName();
	}

	private static Object[] prettyPrint(Class... classes) {
		String[] result = new String[classes.length];
		for(int i=0;i... classes) {
		if(LOGGER.isDebugEnabled()) {
			LOGGER.debug(message,prettyPrint(classes));
		}
	}

	private static void warn(String message, Class... classes) {
		if(LOGGER.isWarnEnabled()) {
			LOGGER.warn(message,prettyPrint(classes));
		}
	}

	private static void trace(String message, Class... classes) {
		if(LOGGER.isTraceEnabled()) {
			LOGGER.trace(message,prettyPrint(classes));
		}
	}

	@SuppressWarnings("unchecked")
	private static synchronized  ObjectFactory findObjectFactory(final Class valueClass) {
		ObjectFactory rawResult=FACTORY_CACHE.get(valueClass);
		if(rawResult==null) {
			debug("No cached factory found for value class '{}'",valueClass);
			rawResult=discoverObjectFactory(valueClass);
			if(rawResult==null) {
				debug("No factory discovered for value class '{}'",valueClass);
				rawResult=prepareDefaultObjectFactory(valueClass);
			}
		}
		return (ObjectFactory)rawResult;
	}

	private static  ObjectFactory discoverObjectFactory(final Class valueClass) {
		ObjectFactory result=null;
		@SuppressWarnings("rawtypes")
		ServiceLoader loader=ServiceLoader.load(ObjectFactory.class);
		@SuppressWarnings("rawtypes")
		Iterator it = loader.iterator();
		do {
			try {
				result=processObjectFactory(valueClass,(ObjectFactory)it.next());
			} catch (ServiceConfigurationError e) {
				LOGGER.error("ObjectFactory configuration failure. Full stacktrace follows",e);
			}
		} while(result==null && it.hasNext());
		return result;
	}

	private static  ObjectFactory processObjectFactory(final Class valueClass, ObjectFactory candidate) {
		ObjectFactory result=null;
		if(PRELOADED_FACTORIES.contains(candidate.getClass())) {
			trace("Discarded cached factory '{}' for value class '{}'",candidate.getClass(),valueClass);
		} else {
			Class targetClass = candidate.targetClass();
			if(FACTORY_CACHE.containsKey(targetClass)) {
				warn("Discarded clashing factory '{}' for value class '{}'",candidate.getClass(),valueClass);
			} else {
				cacheObjectFactory(candidate);
				if(targetClass==valueClass) {
					result=candidate;
					debug("Found factory '{}' for value class '{}'",candidate.getClass(),valueClass);
				} else {
					trace("Discarded factory '{}': target class '{}' does not match value class '{}'",candidate.getClass(),targetClass,valueClass);
				}
			}
		}
		return result;
	}

	private static void cacheObjectFactory(final ObjectFactory candidate) {
		FACTORY_CACHE.put(candidate.targetClass(),candidate);
		PRELOADED_FACTORIES.add(candidate.getClass());
		trace("Cached factory '{}' for value class '{}'",candidate.getClass(),candidate.targetClass());
	}

	@SuppressWarnings("unchecked")
	private static  ObjectFactory prepareDefaultObjectFactory(final Class valueClass) {
		ObjectFactory result=null;
		if(valueClass.isEnum()) {
			result=EnumObjectFactory.create(valueClass.asSubclass(Enum.class));
			debug("No factory found for enum value class '{}'",valueClass);
		} else if(valueClass.isPrimitive()) {
			result=PrimitiveObjectFactory.create(valueClass);
			debug("No factory found for primitive value class '{}'",valueClass);
		} else {
			result=createConventionObjectFactory(valueClass);
		}
		FACTORY_CACHE.put(valueClass,result);
		trace("Cached default factory '{}' for value class '{}'",result.getClass(),valueClass);
		return result;
	}

	private static  ObjectFactory createConventionObjectFactory(final Class valueClass) {
		ObjectFactory result=null;
		result=createReflectionObjectFactory(valueClass,"valueOf");
		if(result!=null) {
			return result;
		}
		result=createReflectionObjectFactory(valueClass,"fromString");
		if(result!=null) {
			return result;
		}
		result=findCompatibleSupertypeObjectFactory(valueClass);
		if(result!=null) {
			return result;
		}
		warn("No factory found for value class '{}'",valueClass);
		return new NullObjectFactory(valueClass);
	}

	private static  ObjectFactory createReflectionObjectFactory(final Class valueClass, String methodName) {
		ObjectFactory result=null;
		try {
			final Method method = valueClass.getDeclaredMethod(methodName, String.class);
			if(isPublicStatic(method) && hasCompatibleReturnType(method, valueClass)) {
				result=new ReflectionObjectFactory(valueClass, method);
				debug("Created "+methodName+"(String) compatible object factory for class '{}'",valueClass);
			}
		} catch (Exception e) {
			LOGGER.trace("Could not create {}(String) compatible object factory for class '{}'",methodName,prettyPrint(valueClass),e);
		}
		return result;
	}

	private static  boolean hasCompatibleReturnType(final Method method, final Class valueClass) {
		return valueClass.isAssignableFrom(method.getReturnType());
	}

	private static boolean isPublicStatic(final Method method) {
		final int modifiers = method.getModifiers();
		return Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers);
	}

	private static  ObjectFactory findCompatibleSupertypeObjectFactory(final Class valueClass) {
		ObjectFactory result=null;
		for(Entry,ObjectFactory> entry:FACTORY_CACHE.entrySet()) {
			if(entry.getKey().isAssignableFrom(valueClass)) {
				result=entry.getValue();
				debug("No factory found for value class '{}', using supertype object factory",valueClass);
				break;
			}
		}
		return result;
	}

	static  boolean isSupported(Class clazz) {
		checkNotNull(clazz,"Class cannot be null");
		return !NullObjectFactory.class.isInstance(ObjectUtil.findObjectFactory(clazz));
	}

	static  T fromString(Class clazz, String str) {
		checkNotNull(clazz,"Class cannot be null");
		checkNotNull(str,"Raw value cannot be null");
		return ObjectUtil.findObjectFactory(clazz).fromString(str);
	}

	static String toString(Object value) {
		checkNotNull(value,"Value cannot be null");
		return ObjectUtil.findObjectFactory(value.getClass()).toString(value);
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy