org.bson.codecs.pojo.TypeData Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mongo-java-driver Show documentation
Show all versions of mongo-java-driver Show documentation
The MongoDB Java Driver uber-artifact, containing the legacy driver, the mongodb-driver, mongodb-driver-core, and bson
/*
* Copyright 2008-present MongoDB, Inc.
*
* 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 org.bson.codecs.pojo;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.lang.String.format;
import static org.bson.assertions.Assertions.notNull;
import static org.bson.codecs.pojo.PropertyReflectionUtils.isGetter;
final class TypeData implements TypeWithTypeParameters {
private final Class type;
private final List> typeParameters;
/**
* Creates a new builder for ClassTypeData
*
* @param type the class for the type
* @param the type
* @return the builder
*/
public static Builder builder(final Class type) {
return new Builder(notNull("type", type));
}
public static TypeData newInstance(final Method method) {
if (isGetter(method)) {
return newInstance(method.getGenericReturnType(), method.getReturnType());
} else {
return newInstance(method.getGenericParameterTypes()[0], method.getParameterTypes()[0]);
}
}
public static TypeData newInstance(final Field field) {
return newInstance(field.getGenericType(), field.getType());
}
public static TypeData newInstance(final Type genericType, final Class clazz) {
TypeData.Builder builder = TypeData.builder(clazz);
if (genericType instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) genericType;
for (Type argType : pType.getActualTypeArguments()) {
getNestedTypeData(builder, argType);
}
}
return builder.build();
}
@SuppressWarnings({"unchecked", "rawtypes"})
private static void getNestedTypeData(final TypeData.Builder builder, final Type type) {
if (type instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) type;
TypeData.Builder paramBuilder = TypeData.builder((Class) pType.getRawType());
for (Type argType : pType.getActualTypeArguments()) {
getNestedTypeData(paramBuilder, argType);
}
builder.addTypeParameter(paramBuilder.build());
} else if (type instanceof TypeVariable) {
builder.addTypeParameter(TypeData.builder(Object.class).build());
} else if (type instanceof Class) {
builder.addTypeParameter(TypeData.builder((Class) type).build());
}
}
/**
* @return the class this {@code ClassTypeData} represents
*/
@Override
public Class getType() {
return type;
}
/**
* @return the type parameters for the class
*/
@Override
public List> getTypeParameters() {
return typeParameters;
}
/**
* A builder for TypeData
*
* @param the main type
*/
public static final class Builder {
private final Class type;
private final List> typeParameters = new ArrayList>();
private Builder(final Class type) {
this.type = type;
}
/**
* Adds a type parameter
*
* @param typeParameter the type parameter
* @param the type of the type parameter
* @return this
*/
public Builder addTypeParameter(final TypeData typeParameter) {
typeParameters.add(notNull("typeParameter", typeParameter));
return this;
}
/**
* Adds multiple type parameters
*
* @param typeParameters the type parameters
* @return this
*/
public Builder addTypeParameters(final List> typeParameters) {
notNull("typeParameters", typeParameters);
for (TypeData typeParameter : typeParameters) {
addTypeParameter(typeParameter);
}
return this;
}
/**
* @return the class type data
*/
public TypeData build() {
return new TypeData(type, Collections.unmodifiableList(typeParameters));
}
}
@Override
public String toString() {
String typeParams = typeParameters.isEmpty() ? ""
: ", typeParameters=[" + nestedTypeParameters(typeParameters) + "]";
return "TypeData{"
+ "type=" + type.getSimpleName()
+ typeParams
+ "}";
}
private static String nestedTypeParameters(final List> typeParameters) {
StringBuilder builder = new StringBuilder();
int count = 0;
int last = typeParameters.size();
for (TypeData typeParameter : typeParameters) {
count++;
builder.append(typeParameter.getType().getSimpleName());
if (!typeParameter.getTypeParameters().isEmpty()) {
builder.append(format("<%s>", nestedTypeParameters(typeParameter.getTypeParameters())));
}
if (count < last) {
builder.append(", ");
}
}
return builder.toString();
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof TypeData)) {
return false;
}
TypeData that = (TypeData) o;
if (!getType().equals(that.getType())) {
return false;
}
if (!getTypeParameters().equals(that.getTypeParameters())) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = getType().hashCode();
result = 31 * result + getTypeParameters().hashCode();
return result;
}
private TypeData(final Class type, final List> typeParameters) {
this.type = boxType(type);
this.typeParameters = typeParameters;
}
boolean isAssignableFrom(final Class cls) {
return type.isAssignableFrom(boxType(cls));
}
@SuppressWarnings("unchecked")
private Class boxType(final Class clazz) {
if (clazz.isPrimitive()) {
return (Class) PRIMITIVE_CLASS_MAP.get(clazz);
} else {
return clazz;
}
}
private static final Map, Class> PRIMITIVE_CLASS_MAP;
static {
Map, Class> map = new HashMap, Class>();
map.put(boolean.class, Boolean.class);
map.put(byte.class, Byte.class);
map.put(char.class, Character.class);
map.put(double.class, Double.class);
map.put(float.class, Float.class);
map.put(int.class, Integer.class);
map.put(long.class, Long.class);
map.put(short.class, Short.class);
PRIMITIVE_CLASS_MAP = map;
}
}