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
The newest version!
/*
* 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.math.BigInteger;
import java.net.URI;
import java.net.URL;
import java.util.Calendar;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
import static java.util.Arrays.asList;
public final class Classes
{
private static final Logger logger = LoggerFactory.getLogger(Classes.class);
private static final Set> PRIMITIVE_WRAPPER_TYPES = getPrimitiveWrapperTypes();
private static final Collection> PRIMITIVE_NUMERIC_TYPES = getPrimitiveNumericTypes();
// private static final List> COMPARABLE_TYPES = Arrays.asList(
// // The Extendables
// // since Java 1.8
// Duration.class,
// Instant.class,
// LocalDate.class,
// LocalDateTime.class,
// LocalTime.class,
// MonthDay.class,
// OffsetDateTime.class,
// OffsetTime.class,
// Year.class,
// YearMonth.class,
// ZonedDateTime.class,
// ZoneOffset.class
// );
@SuppressWarnings("unchecked")
private static final Collection> EXTENDABLE_SIMPLE_TYPES = asList(
BigDecimal.class,
BigInteger.class,
CharSequence.class, // String, StringBuilder, etc.
Calendar.class, // GregorianCalendar, etc.
Date.class, // java.sql.Date, java.util.Date, java.util.Time, etc.
Enum.class // enums... duh
);
@SuppressWarnings("unchecked")
private static final List> FINAL_SIMPLE_TYPES = asList(
Class.class,
URI.class,
URL.class,
Locale.class,
UUID.class
);
private Classes()
{
}
private static Set> getPrimitiveWrapperTypes()
{
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);
//
// The following are not numeric types and tests fail when they are commented in. Feels weird...
//
// numericTypes.add(boolean.class);
// numericTypes.add(void.class);
//
return numericTypes;
}
public static boolean isComparableType(final Class> clazz)
{
return Comparable.class.isAssignableFrom(clazz);
}
public static boolean isSimpleType(final Class> clazz)
{
if (clazz == null)
{
return false;
}
else if (isPrimitiveType(clazz))
{
return true;
}
else if (isPrimitiveWrapperType(clazz))
{
return true;
}
for (final Class> type : FINAL_SIMPLE_TYPES)
{
if (type.equals(clazz))
{
return true;
}
}
for (final Class> type : EXTENDABLE_SIMPLE_TYPES)
{
if (type.isAssignableFrom(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 && PRIMITIVE_WRAPPER_TYPES.contains(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;
}
}
}
}