Package com.github.nmorel.gwtjackson.rebind

Source Code of com.github.nmorel.gwtjackson.rebind.AbstractBeanJsonCreator$TypeParameters

/*
* Copyright 2013 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.rebind;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map.Entry;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonFormat.Shape;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
import com.fasterxml.jackson.annotation.ObjectIdGenerator;
import com.github.nmorel.gwtjackson.client.deser.bean.AbstractIdentityDeserializationInfo;
import com.github.nmorel.gwtjackson.client.deser.bean.AbstractObjectBeanJsonDeserializer;
import com.github.nmorel.gwtjackson.client.deser.bean.AbstractSerializableBeanJsonDeserializer;
import com.github.nmorel.gwtjackson.client.deser.bean.PropertyIdentityDeserializationInfo;
import com.github.nmorel.gwtjackson.client.ser.bean.AbstractBeanJsonSerializer;
import com.github.nmorel.gwtjackson.client.ser.bean.AbstractIdentitySerializationInfo;
import com.github.nmorel.gwtjackson.client.ser.bean.ObjectIdSerializer;
import com.github.nmorel.gwtjackson.client.ser.bean.PropertyIdentitySerializationInfo;
import com.github.nmorel.gwtjackson.rebind.bean.BeanIdentityInfo;
import com.github.nmorel.gwtjackson.rebind.bean.BeanInfo;
import com.github.nmorel.gwtjackson.rebind.bean.BeanProcessor;
import com.github.nmorel.gwtjackson.rebind.bean.BeanTypeInfo;
import com.github.nmorel.gwtjackson.rebind.exception.UnsupportedTypeException;
import com.github.nmorel.gwtjackson.rebind.property.PropertyInfo;
import com.github.nmorel.gwtjackson.rebind.property.processor.PropertyProcessor;
import com.github.nmorel.gwtjackson.rebind.type.JDeserializerType;
import com.github.nmorel.gwtjackson.rebind.type.JMapperType;
import com.github.nmorel.gwtjackson.rebind.type.JSerializerType;
import com.google.gwt.core.ext.GeneratorContext;
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.JClassType;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.thirdparty.guava.common.base.Optional;
import com.google.gwt.thirdparty.guava.common.base.Strings;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableList;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableMap;
import com.google.gwt.user.rebind.SourceWriter;

import static com.github.nmorel.gwtjackson.rebind.CreatorUtils.QUOTED_FUNCTION;
import static com.github.nmorel.gwtjackson.rebind.CreatorUtils.isObject;
import static com.github.nmorel.gwtjackson.rebind.CreatorUtils.isSerializable;

/**
* @author Nicolas Morel
*/
public abstract class AbstractBeanJsonCreator extends AbstractCreator {

    protected static class TypeParameters {

        private final List<String> typeParameterMapperNames;

        private final String joinedTypeParameterMappersWithType;

        public TypeParameters( List<String> typeParameterMapperNames, String joinedTypeParameterMappersWithType ) {
            this.typeParameterMapperNames = typeParameterMapperNames;
            this.joinedTypeParameterMappersWithType = joinedTypeParameterMappersWithType;
        }

        public List<String> getTypeParameterMapperNames() {
            return typeParameterMapperNames;
        }

        public String getJoinedTypeParameterMappersWithType() {
            return joinedTypeParameterMappersWithType;
        }
    }

    protected static final String TYPE_PARAMETER_PREFIX = "p_";

    protected static final String ABSTRACT_BEAN_JSON_DESERIALIZER_CLASS = "com.github.nmorel.gwtjackson.client.deser.bean" + "" +
            ".AbstractBeanJsonDeserializer";

    protected static final String ABSTRACT_BEAN_JSON_SERIALIZER_CLASS = "com.github.nmorel.gwtjackson.client.ser.bean" + "" +
            ".AbstractBeanJsonSerializer";

    protected static final String TYPE_DESERIALIZATION_INFO_CLASS = "com.github.nmorel.gwtjackson.client.deser.bean" + "" +
            ".TypeDeserializationInfo";

    protected static final String TYPE_SERIALIZATION_INFO_CLASS = "com.github.nmorel.gwtjackson.client.ser.bean" + "" +
            ".TypeSerializationInfo";

    protected BeanJsonMapperInfo mapperInfo;

    public AbstractBeanJsonCreator( TreeLogger logger, GeneratorContext context, RebindConfiguration configuration,
                                    JacksonTypeOracle typeOracle ) {
        super( logger, context, configuration, typeOracle );
    }

    @Override
    protected Optional<BeanJsonMapperInfo> getMapperInfo() {
        return Optional.of( mapperInfo );
    }

    /**
     * Creates an implementation of {@link AbstractBeanJsonSerializer} for the type given in
     * parameter
     *
     * @param beanType type of the bean
     *
     * @return the fully qualified name of the created class
     * @throws com.google.gwt.core.ext.UnableToCompleteException
     */
    public String create( JClassType beanType ) throws UnableToCompleteException, UnsupportedTypeException {

        boolean samePackage = true;
        String packageName = beanType.getPackage().getName();
        if ( packageName.startsWith( "java." ) ) {
            packageName = "gwtjackson." + packageName;
            samePackage = false;
        }

        // we concatenate the name of all the enclosing class
        StringBuilder builder = new StringBuilder( beanType.getSimpleSourceName() );
        JClassType enclosingType = beanType.getEnclosingType();
        while ( null != enclosingType ) {
            builder.insert( 0, enclosingType.getSimpleSourceName() + "_" );
            enclosingType = enclosingType.getEnclosingType();
        }

        // if the type is specific to the mapper, we concatenate the name and hash of the mapper to it
        if ( configuration.isSpecificToMapper( beanType ) ) {
            JClassType rootMapperClass = configuration.getRootMapperClass();
            builder.insert( 0, '_' ).insert( 0, configuration.getRootMapperHash() ).insert( 0, '_' ).insert( 0, rootMapperClass
                    .getSimpleSourceName() );
        }

        String simpleSerializerClassName = builder.toString() + "BeanJsonSerializerImpl";
        String qualifiedSerializerClassName = packageName + "." + simpleSerializerClassName;
        String simpleDeserializerClassName = builder.toString() + "BeanJsonDeserializerImpl";
        String qualifiedDeserializerClassName = packageName + "." + simpleDeserializerClassName;

        String qualifiedClassName = isSerializer() ? qualifiedSerializerClassName : qualifiedDeserializerClassName;

        PrintWriter printWriter = getPrintWriter( packageName, isSerializer() ? simpleSerializerClassName : simpleDeserializerClassName );
        // the class already exists, no need to continue
        if ( printWriter == null ) {
            return qualifiedClassName;
        }

        try {
            mapperInfo = typeOracle.getBeanJsonMapperInfo( beanType );

            if ( null == mapperInfo ) {
                // retrieve the informations on the beans and its properties
                BeanInfo beanInfo = BeanProcessor.processBean( logger, typeOracle, configuration, beanType );

                ImmutableMap<String, PropertyInfo> properties = PropertyProcessor
                        .findAllProperties( configuration, logger, typeOracle, beanInfo, samePackage );

                mapperInfo = new BeanJsonMapperInfo( beanType, qualifiedSerializerClassName, simpleSerializerClassName,
                        qualifiedDeserializerClassName, simpleDeserializerClassName, beanInfo, properties );

                typeOracle.addBeanJsonMapperInfo( beanType, mapperInfo );
            }

            String superclass;
            if ( isSerializer() ) {
                superclass = ABSTRACT_BEAN_JSON_SERIALIZER_CLASS + "<" + beanType.getParameterizedQualifiedSourceName() + ">";
            } else if ( isObject( beanType ) ) {
                superclass = AbstractObjectBeanJsonDeserializer.class.getCanonicalName();
            } else if ( isSerializable( beanType ) ) {
                superclass = AbstractSerializableBeanJsonDeserializer.class.getCanonicalName();
            } else {
                superclass = ABSTRACT_BEAN_JSON_DESERIALIZER_CLASS + "<" + beanType.getParameterizedQualifiedSourceName() + ">";
            }

            SourceWriter source = getSourceWriter( printWriter, packageName, getSimpleClassName() + getGenericClassBoundedParameters(),
                    superclass );

            writeClassBody( source, mapperInfo.getBeanInfo(), mapperInfo.getProperties() );

            source.println();
            source.commit( logger );
        } finally {
            printWriter.close();
        }

        return qualifiedClassName;
    }

    protected abstract boolean isSerializer();

    protected String getSimpleClassName() {
        if ( isSerializer() ) {
            return mapperInfo.getSimpleSerializerClassName();
        } else {
            return mapperInfo.getSimpleDeserializerClassName();
        }
    }

    protected String getGenericClassBoundedParameters() {
        return mapperInfo.getGenericClassBoundedParameters();
    }

    protected abstract void writeClassBody( SourceWriter source, BeanInfo info, ImmutableMap<String,
            PropertyInfo> properties ) throws UnableToCompleteException, UnsupportedTypeException;

    protected TypeParameters generateTypeParameterMapperFields( SourceWriter source, BeanInfo beanInfo, String mapperClass,
                                                                String mapperNameFormat ) throws UnableToCompleteException {
        if ( beanInfo.getParameterizedTypes().isEmpty() ) {
            return null;
        }

        List<String> typeParameterMapperNames = new ArrayList<String>();
        StringBuilder joinedTypeParameterMappersWithType = new StringBuilder();

        for ( int i = 0; i < beanInfo.getParameterizedTypes().size(); i++ ) {
            if ( i > 0 ) {
                joinedTypeParameterMappersWithType.append( ", " );
            }

            JClassType argType = beanInfo.getParameterizedTypes().get( i );
            String mapperType = String.format( "%s<%s>", mapperClass, argType.getName() );
            String mapperName = String.format( mapperNameFormat, i );

            source.println( "private final %s %s;", mapperType, mapperName );

            typeParameterMapperNames.add( mapperName );
            joinedTypeParameterMappersWithType.append( String.format( "%s %s%s", mapperType, TYPE_PARAMETER_PREFIX, mapperName ) );
        }

        return new TypeParameters( typeParameterMapperNames, joinedTypeParameterMappersWithType.toString() );
    }

    protected String getParameterizedQualifiedClassName( JType type ) {
        if ( null == type.isPrimitive() ) {
            return type.getParameterizedQualifiedSourceName();
        } else {
            return type.isPrimitive().getQualifiedBoxedSourceName();
        }
    }

    protected String getQualifiedClassName( JType type ) {
        if ( null == type.isPrimitive() ) {
            return type.getQualifiedSourceName();
        } else {
            return type.isPrimitive().getQualifiedBoxedSourceName();
        }
    }

    protected Optional<JSerializerType> getIdentitySerializerType( BeanIdentityInfo identityInfo ) throws UnableToCompleteException,
            UnsupportedTypeException {
        if ( identityInfo.isIdABeanProperty() ) {
            return Optional.absent();
        } else {
            return Optional.of( getJsonSerializerFromType( identityInfo.getType().get() ) );
        }
    }

    protected void generateIdentifierSerializationInfo( SourceWriter source, JClassType type, BeanIdentityInfo identityInfo,
                                                        Optional<JSerializerType> serializerType ) throws UnableToCompleteException {

        if ( identityInfo.isIdABeanProperty() ) {
            source.print( "new %s<%s>(%s, \"%s\")", PropertyIdentitySerializationInfo.class.getName(), type
                    .getParameterizedQualifiedSourceName(), identityInfo.isAlwaysAsId(), identityInfo.getPropertyName() );
        } else {
            String qualifiedType = getParameterizedQualifiedClassName( identityInfo.getType().get() );
            String identityPropertyClass = String.format( "%s<%s, %s>", AbstractIdentitySerializationInfo.class.getName(), type
                    .getParameterizedQualifiedSourceName(), qualifiedType );

            source.println( "new %s(%s, \"%s\") {", identityPropertyClass, identityInfo.isAlwaysAsId(), identityInfo.getPropertyName() );
            source.indent();

            source.println( "@Override" );
            source.println( "protected %s<?> newSerializer() {", JSON_SERIALIZER_CLASS );
            source.indent();
            source.println( "return %s;", serializerType.get().getInstance() );
            source.outdent();
            source.println( "}" );
            source.println();

            source.println( "@Override" );
            source.println( "public %s<%s> getObjectId(%s bean, %s ctx) {", ObjectIdSerializer.class.getName(), qualifiedType, type
                    .getParameterizedQualifiedSourceName(), JSON_SERIALIZATION_CONTEXT_CLASS );
            source.indent();

            String generatorType = String.format( "%s<%s>", ObjectIdGenerator.class.getName(), qualifiedType );
            source.println( "%s generator = new %s().forScope(%s.class);", generatorType, identityInfo.getGenerator()
                    .getCanonicalName(), identityInfo.getScope().getName() );
            source.println( "%s scopedGen = ctx.findObjectIdGenerator(generator);", generatorType );
            source.println( "if(null == scopedGen) {" );
            source.indent();
            source.println( "scopedGen = generator.newForSerialization(ctx);" );
            source.println( "ctx.addGenerator(scopedGen);" );
            source.outdent();
            source.println( "}" );
            source.println( "return new %s<%s>(scopedGen.generateId(bean), getSerializer());", ObjectIdSerializer.class
                    .getName(), qualifiedType );

            source.outdent();
            source.println( "}" );

            source.outdent();
            source.print( "}" );
        }
    }

    protected Optional<JDeserializerType> getIdentityDeserializerType( BeanIdentityInfo identityInfo ) throws UnableToCompleteException,
            UnsupportedTypeException {
        if ( identityInfo.isIdABeanProperty() ) {
            return Optional.absent();
        } else {
            return Optional.of( getJsonDeserializerFromType( identityInfo.getType().get() ) );
        }
    }

    protected void generateIdentifierDeserializationInfo( SourceWriter source, JClassType type, BeanIdentityInfo identityInfo,
                                                          Optional<JDeserializerType> deserializerType ) throws UnableToCompleteException {
        if ( identityInfo.isIdABeanProperty() ) {

            source.print( "new %s<%s>(\"%s\", %s.class, %s.class)", PropertyIdentityDeserializationInfo.class.getName(), type
                    .getParameterizedQualifiedSourceName(), identityInfo.getPropertyName(), identityInfo.getGenerator()
                    .getCanonicalName(), identityInfo.getScope().getCanonicalName() );

        } else {

            String qualifiedType = getParameterizedQualifiedClassName( identityInfo.getType().get() );

            String identityPropertyClass = String.format( "%s<%s, %s>", AbstractIdentityDeserializationInfo.class.getName(), type
                    .getParameterizedQualifiedSourceName(), qualifiedType );

            source.println( "new %s(\"%s\", %s.class, %s.class) {", identityPropertyClass, identityInfo.getPropertyName(), identityInfo
                    .getGenerator().getCanonicalName(), identityInfo.getScope().getCanonicalName() );
            source.indent();

            source.println( "@Override" );
            source.println( "protected %s<?> newDeserializer() {", JSON_DESERIALIZER_CLASS );
            source.indent();
            source.println( "return %s;", deserializerType.get().getInstance() );
            source.outdent();
            source.println( "}" );

            source.outdent();
            source.print( "}" );
        }
    }

    protected void generateTypeInfo( SourceWriter source, BeanTypeInfo typeInfo, boolean serialization ) throws UnableToCompleteException {
        String typeInfoProperty = null;
        if ( null != typeInfo.getPropertyName() ) {
            typeInfoProperty = QUOTED_FUNCTION.apply( typeInfo.getPropertyName() );
        }
        source.println( "new %s(%s.%s, %s)", serialization ? TYPE_SERIALIZATION_INFO_CLASS : TYPE_DESERIALIZATION_INFO_CLASS, As.class
                .getCanonicalName(), typeInfo.getInclude(), typeInfoProperty );
        source.indent();

        ImmutableMap<JClassType, String> mapTypeToMetadata;
        if ( serialization ) {
            mapTypeToMetadata = typeInfo.getMapTypeToSerializationMetadata();
        } else {
            mapTypeToMetadata = typeInfo.getMapTypeToDeserializationMetadata();
        }

        for ( Entry<JClassType, String> entry : mapTypeToMetadata.entrySet() ) {
            source.println( ".addTypeInfo(%s.class, \"%s\")", entry.getKey().getQualifiedSourceName(), entry.getValue() );
        }

        source.outdent();
    }

    protected JClassType findFirstTypeToApplyPropertyAnnotation( JMapperType mapperType ) {
        return findFirstTypeToApplyPropertyAnnotation( Arrays.asList( mapperType ) );
    }

    private JClassType findFirstTypeToApplyPropertyAnnotation( List<JMapperType> mapperTypeList ) {
        if ( mapperTypeList.isEmpty() ) {
            return null;
        }

        List<JMapperType> subLevel = new ArrayList<JMapperType>();
        for ( JMapperType mapperType : mapperTypeList ) {
            if ( mapperType.isBeanMapper() ) {
                return mapperType.getType().isClass();
            } else if ( mapperType.getParameters().size() > 0 ) {
                subLevel.addAll( mapperType.getParameters() );
            }
        }

        return findFirstTypeToApplyPropertyAnnotation( subLevel );
    }

    protected void generateCommonPropertyParameters( SourceWriter source, PropertyInfo property,
                                                     JMapperType mapperType ) throws UnableToCompleteException {
        if ( property.getFormat().isPresent() ) {
            JsonFormat format = property.getFormat().get();
            if ( !Strings.isNullOrEmpty( format.pattern() ) ) {
                source.println();
                source.print( ".setPattern(\"%s\")", format.pattern() );
            }
            source.println();
            source.print( ".setShape(%s.%s)", Shape.class.getCanonicalName(), format.shape().name() );
            if ( !Strings.isNullOrEmpty( format.locale() ) && !JsonFormat.DEFAULT_LOCALE.equals( format.locale() ) ) {
                logger.log( Type.WARN, "JsonFormat.locale is not supported by default" );
                source.println();
                source.print( ".setLocale(\"%s\")", format.locale() );
            }
            if ( !Strings.isNullOrEmpty( format.timezone() ) && !JsonFormat.DEFAULT_TIMEZONE.equals( format.timezone() ) ) {
                logger.log( Type.WARN, "JsonFormat.timezone is not supported by default" );
                source.println();
                source.print( ".setTimezone(\"%s\")", format.timezone() );
            }
        }
        if ( property.getIgnoredProperties().isPresent() ) {
            for ( String ignoredProperty : property.getIgnoredProperties().get() ) {
                source.println();
                source.print( ".addIgnoredProperty(\"%s\")", ignoredProperty );
            }
        }
    }

    protected ImmutableList<JClassType> filterSubtypes( BeanInfo beanInfo ) {
        if ( isSerializer() ) {
            return CreatorUtils.filterSubtypesForSerialization( logger, configuration, beanInfo.getType() );
        } else {
            return CreatorUtils.filterSubtypesForDeserialization( logger, configuration, beanInfo.getType() );
        }
    }
}
TOP

Related Classes of com.github.nmorel.gwtjackson.rebind.AbstractBeanJsonCreator$TypeParameters

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.