de.danielbechler.util.Classes Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-object-diff Show documentation
Show all versions of java-object-diff Show documentation
Framework to detect and handle differences between Java objects
/*
* Copyright 2014 Daniel Bechler
*
* 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.
*/
package de.danielbechler.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.math.BigDecimal;
import java.net.URI;
import java.net.URL;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.TreeSet;
/**
* @author Daniel Bechler
*/
public final class Classes
{
private static final Logger logger = LoggerFactory.getLogger(Classes.class);
private static final Set> WRAPPER_TYPES = getWrapperTypes();
private static final Collection> PRIMITIVE_NUMERIC_TYPES = getPrimitiveNumericTypes();
private Classes()
{
}
private static Set> getWrapperTypes()
{
final Set> wrapperTypes = new HashSet>();
wrapperTypes.add(Boolean.class);
wrapperTypes.add(Character.class);
wrapperTypes.add(Byte.class);
wrapperTypes.add(Short.class);
wrapperTypes.add(Integer.class);
wrapperTypes.add(Long.class);
wrapperTypes.add(Float.class);
wrapperTypes.add(Double.class);
wrapperTypes.add(Void.class);
return wrapperTypes;
}
public static boolean isPrimitiveNumericType(final Class> clazz)
{
return PRIMITIVE_NUMERIC_TYPES.contains(clazz);
}
private static Collection> getPrimitiveNumericTypes()
{
final Collection> numericTypes = new HashSet>();
numericTypes.add(char.class);
numericTypes.add(byte.class);
numericTypes.add(short.class);
numericTypes.add(int.class);
numericTypes.add(long.class);
numericTypes.add(float.class);
numericTypes.add(double.class);
return numericTypes;
}
public static boolean isSimpleType(final Class> clazz)
{
if (clazz != null)
{
if (isPrimitiveType(clazz))
{
return true;
}
else if (isPrimitiveWrapperType(clazz))
{
return true;
}
else if (clazz.isEnum())
{
return true;
}
else if (CharSequence.class.isAssignableFrom(clazz))
{
return true;
}
else if (Number.class.isAssignableFrom(clazz))
{
return true;
}
else if (Date.class.isAssignableFrom(clazz))
{
return true;
}
else if (URI.class.equals(clazz))
{
return true;
}
else if (URL.class.equals(clazz))
{
return true;
}
else if (Locale.class.equals(clazz))
{
return true;
}
else if (Class.class.equals(clazz))
{
return true;
}
}
return false;
}
public static boolean isPrimitiveType(final Class> clazz)
{
return clazz != null && clazz.isPrimitive();
}
public static boolean isPrimitiveWrapperType(final Class> clazz)
{
return clazz != null && WRAPPER_TYPES.contains(clazz);
}
public static boolean isComparableType(final Class> clazz)
{
return BigDecimal.class.equals(clazz);
}
public static T freshInstanceOf(final Class clazz)
{
if (clazz == null)
{
return null;
}
final Constructor constructor;
try
{
constructor = clazz.getDeclaredConstructor();
}
catch (final NoSuchMethodException e)
{
logger.debug("Missing default constructor for type {}. Assuming standard default values " +
"for primitive properties.", clazz.getName());
return null;
}
final boolean accessibility = constructor.isAccessible();
try
{
constructor.setAccessible(true);
return constructor.newInstance();
}
catch (final Exception e)
{
throw new RuntimeException(e);
}
finally
{
constructor.setAccessible(accessibility);
}
}
public static Set> typesOf(final Object... values)
{
final Set> types = new HashSet>(values.length);
for (final Object value : values)
{
if (value != null)
{
types.add(value.getClass());
}
}
return types;
}
public static boolean allAssignableFrom(final Class> sharedType,
final Iterable extends Class>> types)
{
boolean matching = true;
for (final Class> type : types)
{
if (!sharedType.isAssignableFrom(type))
{
matching = false;
}
}
return matching;
}
/**
* This method will not extract the interfaces of the given types, since there is no way to determine, which
* interface is the most specific one (due to their compositional nature). However, if one or more interfaces
* are given as type, they will be considered as most specific shared type.
*
* @return The most specific class (or interface) shared by all given types.
*/
public static Class> mostSpecificSharedType(final Collection> types)
{
final Collection> potentiallySharedTypes = superclassesOf(types);
potentiallySharedTypes.addAll(types);
final Collection> sharedTypes = new TreeSet>(new ClassComparator());
for (final Class> potentiallySharedType : potentiallySharedTypes)
{
int matches = 0;
for (final Class> type : types)
{
if (potentiallySharedType.isAssignableFrom(type))
{
matches++;
}
}
if (matches == types.size())
{
sharedTypes.add(potentiallySharedType);
}
}
if (sharedTypes.isEmpty())
{
return null;
}
return sharedTypes.iterator().next();
}
private static Collection> superclassesOf(final Iterable> types)
{
final Collection> superclasses = new HashSet>();
for (final Class> type : types)
{
Class> superclass = type.getSuperclass();
while (superclass != null && superclass != Object.class)
{
superclasses.add(superclass);
superclass = superclass.getSuperclass();
}
}
return superclasses;
}
private static class ClassComparator implements Comparator>, Serializable
{
private static final long serialVersionUID = 56568941407903459L;
public int compare(final Class> o1, final Class> o2)
{
if (o1.isAssignableFrom(o2))
{
return 1;
}
else if (o2.isAssignableFrom(o1))
{
return -1;
}
else
{
return 0;
}
}
}
}