Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 10 additions & 29 deletions bson/src/main/org/bson/codecs/pojo/TypeData.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,8 @@ public static TypeData<?> newInstance(final Field field) {
}

public static <T> TypeData<T> newInstance(final Type genericType, final Class<T> clazz) {
TypeData.Builder<T> builder = TypeData.builder(clazz);
if (genericType instanceof ParameterizedType) {
ParameterizedType pType = (ParameterizedType) genericType;
for (Type argType : pType.getActualTypeArguments()) {
getNestedTypeData(builder, argType);
}
}
return builder.build();
// No enclosing class context: type variables have nothing to resolve against and erase to Object.
return newInstance(genericType, clazz, Collections.<TypeVariable<?>>emptyList(), null);
}

static <T> TypeData<T> newInstance(final Type genericParentType, final Class<T> parentClass,
Expand Down Expand Up @@ -110,32 +104,19 @@ private static TypeData<?> resolveTypeArgument(final Type type,
return TypeData.builder(Object.class).build();
} else if (type instanceof Class) {
return TypeData.builder((Class) type).build();
} else if (type instanceof WildcardType) {
// A wildcard cannot be the top-level type argument of an extends/implements clause (JLS §8.1.4,
// §8.1.5), but it can appear nested inside one (e.g. extends Base<List<? extends Number>>) or in a
// field/method generic type. Resolve it to its upper bound so any type variable inside the bound is
// still substituted against the current context.
return resolveTypeArgument(((WildcardType) type).getUpperBounds()[0], currentClassTypeParameters,
currentClassTypeData);
} else {
// WildcardType is intentionally not handled: wildcards cannot appear as type arguments
// in superclass or interface declarations (JLS §8.1.4, §8.1.5), so this method is
// never called with a WildcardType from getClassHierarchy. Falls through to Object below.
// Any other Type (e.g. GenericArrayType) is erased to Object.
return TypeData.builder(Object.class).build();
}
}

@SuppressWarnings({"unchecked", "rawtypes"})
private static <T> void getNestedTypeData(final TypeData.Builder<T> 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 WildcardType) {
builder.addTypeParameter(TypeData.builder((Class) ((WildcardType) type).getUpperBounds()[0]).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
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.bson.codecs.pojo.entities.ForwardingInterfaceModel;
import org.bson.codecs.pojo.entities.ForwardingModel;
import org.bson.codecs.pojo.entities.ForwardingNestedModel;
import org.bson.codecs.pojo.entities.ForwardingWildcardModel;
import org.bson.codecs.pojo.entities.GenericHolderModel;
import org.bson.codecs.pojo.entities.GenericTreeModel;
import org.bson.codecs.pojo.entities.InterfaceBasedModel;
Expand Down Expand Up @@ -549,6 +550,13 @@ private static List<TestData> testCases() {
new ForwardingNestedModel(asList("a", "b", "c")),
getPojoCodecProviderBuilder(ForwardingNestedModel.class),
"{'value': ['a', 'b', 'c']}"));
data.add(new TestData("Forwarding nested wildcard chain resolves to List<? extends ShapeModelAbstract>",
new ForwardingWildcardModel(asList(getShapeModelCircle(), getShapeModelRectangle())),
getPojoCodecProviderBuilder(ForwardingWildcardModel.class, ShapeModelAbstract.class,
ShapeModelCircle.class, ShapeModelRectangle.class),
"{'value': [{'_t': 'org.bson.codecs.pojo.entities.ShapeModelCircle', 'color': 'orange', 'radius': 4.2}, "
+ "{'_t': 'org.bson.codecs.pojo.entities.ShapeModelRectangle', 'color': 'green', 'width': 22.1, "
+ "'height': 105.0}]}"));

return data;
}
Expand Down
25 changes: 20 additions & 5 deletions bson/src/test/unit/org/bson/codecs/pojo/TypeDataTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -181,14 +181,29 @@ public void newInstanceWithEmptyTypeParamsReturnsRawTypeData() throws NoSuchFiel
assertEquals(expected, actual);
}

@Test
public void newInstanceResolvesBoundedWildcardToUpperBound() throws NoSuchFieldException {
Field boundedWildcardField = Holder.class.getDeclaredField("boundedWildcard");
Type boundedWildcardGenericType = boundedWildcardField.getGenericType();

List<TypeVariable<?>> typeParams = asList(Holder.class.getTypeParameters());
TypeData<Holder> currentResolved = TypeData.builder(Holder.class)
.addTypeParameter(TypeData.builder(String.class).build()).build();

TypeData<List> expected = TypeData.builder(List.class)
.addTypeParameter(TypeData.builder(Number.class).build()).build();
TypeData<List> actual = TypeData.newInstance(boundedWildcardGenericType, List.class, typeParams, currentResolved);
assertEquals(expected, actual);
}

private static class Holder<T> {
T scalar;
List<T> list;
List<String> concrete;
Map<String, T> nestedMap;
private List<T> list;
private List<String> concrete;
private Map<String, T> nestedMap;
private List<? extends Number> boundedWildcard;
}

private static class NonGeneric {
String s;
private String s;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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.entities;

import java.util.List;

public class ForwardingWildcardModel extends ForwardingNestedBase<List<? extends ShapeModelAbstract>> {

public ForwardingWildcardModel() {
}

public ForwardingWildcardModel(final List<? extends ShapeModelAbstract> value) {
super(value);
}
}