Skip to content

Commit

Permalink
Fix error with user class implementing Iterable
Browse files Browse the repository at this point in the history
  • Loading branch information
nmorel committed Aug 9, 2014
1 parent 656da80 commit 597992b
Show file tree
Hide file tree
Showing 7 changed files with 311 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,9 @@
import com.fasterxml.jackson.annotation.ObjectIdGenerators.UUIDGenerator;
import com.github.nmorel.gwtjackson.rebind.JacksonTypeOracle;
import com.github.nmorel.gwtjackson.rebind.RebindConfiguration;
import com.github.nmorel.gwtjackson.rebind.property.PropertyAccessors;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JAbstractMethod;
import com.google.gwt.core.ext.typeinfo.JArrayType;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JConstructor;
import com.google.gwt.core.ext.typeinfo.JMethod;
Expand Down Expand Up @@ -111,8 +108,8 @@ public static BeanInfo processBean( TreeLogger logger, JacksonTypeOracle typeOra
}
}

builder.setIdentityInfo( processIdentity( logger, typeOracle, configuration, beanType, null, false ) );
builder.setTypeInfo( processType( logger, typeOracle, configuration, beanType, null, false ) );
builder.setIdentityInfo( processIdentity( logger, typeOracle, configuration, beanType ) );
builder.setTypeInfo( processType( logger, typeOracle, configuration, beanType ) );

return builder.build();
}
Expand Down Expand Up @@ -249,31 +246,18 @@ private static <T extends Annotation> boolean isAllParametersAnnotatedWith( JAbs
return true;
}

public static Optional<BeanIdentityInfo> processIdentity( TreeLogger logger, JacksonTypeOracle typeOracle,
RebindConfiguration configuration, JType type,
PropertyAccessors propertyAccessors ) throws UnableToCompleteException {
Optional<JClassType> classType = extractBeanType( logger, typeOracle, type );
if ( classType.isPresent() ) {
return processIdentity( logger, typeOracle, configuration, classType.get(), propertyAccessors, true );
} else {
return Optional.absent();
}
private static Optional<BeanIdentityInfo> processIdentity( TreeLogger logger, JacksonTypeOracle typeOracle,
RebindConfiguration configuration,
JClassType type ) throws UnableToCompleteException {
return processIdentity( logger, typeOracle, configuration, type, Optional.<JsonIdentityInfo>absent(), Optional
.<JsonIdentityReference>absent() );
}

private static Optional<BeanIdentityInfo> processIdentity( TreeLogger logger, JacksonTypeOracle typeOracle,
public static Optional<BeanIdentityInfo> processIdentity( TreeLogger logger, JacksonTypeOracle typeOracle,
RebindConfiguration configuration, JClassType type,
PropertyAccessors propertyAccessors,
boolean property ) throws UnableToCompleteException {
Optional<JsonIdentityInfo> jsonIdentityInfo = Optional.absent();
Optional<JsonIdentityReference> jsonIdentityReference = Optional.absent();
if ( property ) {
jsonIdentityInfo = propertyAccessors.getAnnotation( JsonIdentityInfo.class );
jsonIdentityReference = propertyAccessors.getAnnotation( JsonIdentityReference.class );
if ( !jsonIdentityInfo.isPresent() && !jsonIdentityReference.isPresent() ) {
// no override on field
return Optional.absent();
}
}
Optional<JsonIdentityInfo> jsonIdentityInfo,
Optional<JsonIdentityReference> jsonIdentityReference ) throws
UnableToCompleteException {

if ( !jsonIdentityInfo.isPresent() ) {
jsonIdentityInfo = findFirstEncounteredAnnotationsOnAllHierarchy( configuration, type, JsonIdentityInfo.class );
Expand Down Expand Up @@ -318,30 +302,16 @@ private static Optional<BeanIdentityInfo> processIdentity( TreeLogger logger, Ja
return Optional.absent();
}

public static Optional<BeanTypeInfo> processType( TreeLogger logger, JacksonTypeOracle typeOracle, RebindConfiguration configuration,
JType type, PropertyAccessors propertyAccessors ) throws UnableToCompleteException {
Optional<JClassType> classType = extractBeanType( logger, typeOracle, type );
if ( classType.isPresent() ) {
return processType( logger, typeOracle, configuration, classType.get(), propertyAccessors, true );
} else {
return Optional.absent();
}
private static Optional<BeanTypeInfo> processType( TreeLogger logger, JacksonTypeOracle typeOracle,
RebindConfiguration configuration,
JClassType type ) throws UnableToCompleteException {
return processType( logger, typeOracle, configuration, type, Optional.<JsonTypeInfo>absent(), Optional.<JsonSubTypes>absent() );
}

private static Optional<BeanTypeInfo> processType( TreeLogger logger, JacksonTypeOracle typeOracle,
public static Optional<BeanTypeInfo> processType( TreeLogger logger, JacksonTypeOracle typeOracle,
RebindConfiguration configuration, JClassType type,
PropertyAccessors propertyAccessors,
boolean property ) throws UnableToCompleteException {
Optional<JsonTypeInfo> jsonTypeInfo = Optional.absent();
Optional<JsonSubTypes> propertySubTypes = Optional.absent();
if ( property ) {
jsonTypeInfo = propertyAccessors.getAnnotation( JsonTypeInfo.class );
propertySubTypes = propertyAccessors.getAnnotation( JsonSubTypes.class );
if ( !jsonTypeInfo.isPresent() && !propertySubTypes.isPresent() ) {
// no override on field
return Optional.absent();
}
}
Optional<JsonTypeInfo> jsonTypeInfo, Optional<JsonSubTypes> propertySubTypes )
throws UnableToCompleteException {

if ( !jsonTypeInfo.isPresent() ) {
jsonTypeInfo = findFirstEncounteredAnnotationsOnAllHierarchy( configuration, type, JsonTypeInfo.class );
Expand Down Expand Up @@ -455,39 +425,4 @@ private static JsonSubTypes.Type findTypeOnSubTypes( JClassType subtype, Optiona
return null;
}

/**
* Extract the bean type from the type given in parameter. For {@link java.util.Collection}, it gives the bounded type. For {@link
* java.util.Map}, it gives the second bounded type. Otherwise, it gives the type given in parameter.
*
* @param type type to extract the bean type
*
* @return the extracted type
*/
private static Optional<JClassType> extractBeanType( TreeLogger logger, JacksonTypeOracle typeOracle,
JType type ) throws UnableToCompleteException {
JArrayType arrayType = type.isArray();
if ( null != arrayType ) {
return extractBeanType( logger, typeOracle, arrayType.getComponentType() );
}

JClassType classType = type.isClassOrInterface();
if ( null == classType ) {
return Optional.absent();
} else if ( typeOracle.isIterable( classType ) ) {
if ( null == classType.isParameterized() || classType.isParameterized().getTypeArgs().length != 1 ) {
logger.log( Type.ERROR, "Wrong number of argument for a java.lang.Iterable implementation" );
throw new UnableToCompleteException();
}
return extractBeanType( logger, typeOracle, classType.isParameterized().getTypeArgs()[0] );
} else if ( typeOracle.isMap( classType ) ) {
if ( null == classType.isParameterized() || classType.isParameterized().getTypeArgs().length != 2 ) {
logger.log( Type.ERROR, "Wrong number of argument for a java.util.Map implementation" );
throw new UnableToCompleteException();
}
return extractBeanType( logger, typeOracle, classType.isParameterized().getTypeArgs()[1] );
} else {
return Optional.of( classType );
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,17 @@
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIdentityReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonIgnoreType;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonManagedReference;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonRawValue;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.github.nmorel.gwtjackson.rebind.JacksonTypeOracle;
import com.github.nmorel.gwtjackson.rebind.RebindConfiguration;
import com.github.nmorel.gwtjackson.rebind.bean.BeanInfo;
Expand All @@ -45,6 +49,8 @@
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JArrayType;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JField;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JPrimitiveType;
Expand Down Expand Up @@ -164,8 +170,7 @@ private static Optional<PropertyInfo> processProperty( RebindConfiguration confi
}
determineSetter( propertyAccessors, setterAutoDetected, fieldAutoDetected, builder );

builder.setIdentityInfo( BeanProcessor.processIdentity( logger, typeOracle, configuration, type, propertyAccessors ) );
builder.setTypeInfo( BeanProcessor.processType( logger, typeOracle, configuration, type, propertyAccessors ) );
processBeanAnnotation( logger, typeOracle, configuration, type, propertyAccessors, builder );

builder.setFormat( propertyAccessors.getAnnotation( JsonFormat.class ) );

Expand Down Expand Up @@ -332,4 +337,79 @@ private static void determineSetter( final PropertyAccessors propertyAccessors,
.getField(), setterAutoDetect, propertyAccessors.getSetter() ) ) );
}
}

private static void processBeanAnnotation( TreeLogger logger, JacksonTypeOracle typeOracle, RebindConfiguration configuration,
JType type, PropertyAccessors propertyAccessors,
PropertyInfoBuilder builder ) throws UnableToCompleteException {

// identity
Optional<JsonIdentityInfo> jsonIdentityInfo = propertyAccessors.getAnnotation( JsonIdentityInfo.class );
Optional<JsonIdentityReference> jsonIdentityReference = propertyAccessors.getAnnotation( JsonIdentityReference.class );

// type info
Optional<JsonTypeInfo> jsonTypeInfo = propertyAccessors.getAnnotation( JsonTypeInfo.class );
Optional<JsonSubTypes> propertySubTypes = propertyAccessors.getAnnotation( JsonSubTypes.class );

// if no annotation is present that overrides bean processing, we just stop now
if ( !jsonIdentityInfo.isPresent() && !jsonIdentityReference.isPresent() && !jsonTypeInfo.isPresent() && !propertySubTypes
.isPresent() ) {
// no override on field
return;
}

// we need to find the bean to apply annotation on
Optional<JClassType> beanType = extractBeanType( logger, typeOracle, type, builder.getPropertyName() );

if ( beanType.isPresent() ) {
if ( jsonIdentityInfo.isPresent() || jsonIdentityReference.isPresent() ) {
builder.setIdentityInfo( BeanProcessor.processIdentity( logger, typeOracle, configuration, beanType
.get(), jsonIdentityInfo, jsonIdentityReference ) );
}

if ( jsonTypeInfo.isPresent() || propertySubTypes.isPresent() ) {
builder.setTypeInfo( BeanProcessor.processType( logger, typeOracle, configuration, beanType
.get(), jsonTypeInfo, propertySubTypes ) );
}
} else {
logger.log( Type.WARN, "Annotation present on property " + builder.getPropertyName() + " but no valid bean has been found." );
}
}

/**
* Extract the bean type from the type given in parameter. For {@link java.util.Collection}, it gives the bounded type. For {@link
* java.util.Map}, it gives the second bounded type. Otherwise, it gives the type given in parameter.
*
* @param type type to extract the bean type
* @param propertyName name of the property
*
* @return the extracted type
*/
private static Optional<JClassType> extractBeanType( TreeLogger logger, JacksonTypeOracle typeOracle, JType type,
String propertyName ) {
JArrayType arrayType = type.isArray();
if ( null != arrayType ) {
return extractBeanType( logger, typeOracle, arrayType.getComponentType(), propertyName );
}

JClassType classType = type.isClassOrInterface();
if ( null == classType ) {
return Optional.absent();
} else if ( typeOracle.isIterable( classType ) ) {
if ( null == classType.isParameterized() || classType.isParameterized().getTypeArgs().length != 1 ) {
logger.log( Type.INFO, "Expected one argument for the java.lang.Iterable '" + propertyName + "'. Applying annotations to " +
"type " + classType.getParameterizedQualifiedSourceName() );
return Optional.of( classType );
}
return extractBeanType( logger, typeOracle, classType.isParameterized().getTypeArgs()[0], propertyName );
} else if ( typeOracle.isMap( classType ) ) {
if ( null == classType.isParameterized() || classType.isParameterized().getTypeArgs().length != 2 ) {
logger.log( Type.INFO, "Expected two arguments for the java.util.Map '" + propertyName + "'. Applying annotations to " +
"type " + classType.getParameterizedQualifiedSourceName() );
return Optional.of( classType );
}
return extractBeanType( logger, typeOracle, classType.isParameterized().getTypeArgs()[1], propertyName );
} else {
return Optional.of( classType );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
import com.github.nmorel.gwtjackson.client.mapper.AllCollectionsObjectMapperTest;
import com.github.nmorel.gwtjackson.client.mapper.AllMapsObjectMapperTest;
import com.github.nmorel.gwtjackson.client.mapper.CommonJsonMapperTest;
import com.github.nmorel.gwtjackson.client.mapper.CustomIterableGwtTest;
import com.github.nmorel.gwtjackson.client.mapper.IgnoreStaticGwtTest;
import com.github.nmorel.gwtjackson.client.mapper.SimpleBeanObjectReaderTest;
import com.github.nmorel.gwtjackson.client.mapper.SimpleBeanObjectWriterTest;
Expand Down Expand Up @@ -260,6 +261,7 @@ public static Test suite() {
suite.addTestSuite( AllMapsObjectMapperTest.class );
suite.addTestSuite( CommonJsonMapperTest.class );
suite.addTestSuite( IgnoreStaticGwtTest.class );
suite.addTestSuite( CustomIterableGwtTest.class );

// Annotations test
suite.addTestSuite( JsonAutoDetectGwtTest.class );
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
/*
* Copyright 2014 Nicolas Morel
*
* 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 com.github.nmorel.gwtjackson.client.advanced;

import com.github.nmorel.gwtjackson.client.GwtJacksonTestCase;
Expand All @@ -12,6 +28,7 @@
import com.google.web.bindery.requestfactory.shared.ValueProxy;

/**
* @author Nicolas Morel
*/
public class ProxyAndAnonymousClassSerializationGwtTest extends GwtJacksonTestCase {

Expand Down Expand Up @@ -40,7 +57,7 @@ public void setName( String name ) {
}
}

@ProxyFor( RfBean.class )
@ProxyFor(RfBean.class)
public static interface RfBeanProxy extends ValueProxy {

int getId();
Expand All @@ -59,7 +76,7 @@ public void save( RfBean bean ) {
}
}

@Service( RfBeanService.class )
@Service(RfBeanService.class)
public static interface ServiceContext extends RequestContext {

Request<Void> save( RfBeanProxy bean );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright 2014 Nicolas Morel
*
* 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 com.github.nmorel.gwtjackson.client.mapper;

import com.github.nmorel.gwtjackson.client.GwtJacksonTestCase;
import com.github.nmorel.gwtjackson.client.ObjectMapper;
import com.github.nmorel.gwtjackson.shared.ObjectMapperTester;
import com.github.nmorel.gwtjackson.shared.mapper.CustomIterableTester;
import com.github.nmorel.gwtjackson.shared.mapper.CustomIterableTester.MyIntegerIterable;
import com.github.nmorel.gwtjackson.shared.mapper.CustomIterableTester.Wrapper;
import com.google.gwt.core.client.GWT;

/**
* @author Nicolas Morel
*/
public class CustomIterableGwtTest extends GwtJacksonTestCase {

public interface WrapperMapper extends ObjectMapper<Wrapper>, ObjectMapperTester<Wrapper> {

static WrapperMapper INSTANCE = GWT.create( WrapperMapper.class );
}

public interface MyIntegerIterableMapper extends ObjectMapper<MyIntegerIterable>, ObjectMapperTester<MyIntegerIterable> {

static MyIntegerIterableMapper INSTANCE = GWT.create( MyIntegerIterableMapper.class );
}

public void testCustomIntegerIterable() {
CustomIterableTester.INSTANCE.testCustomIntegerIterable( MyIntegerIterableMapper.INSTANCE );
}

public void testWrapper() {
CustomIterableTester.INSTANCE.testWrapper( WrapperMapper.INSTANCE );
}
}
Loading

0 comments on commit 597992b

Please sign in to comment.