Package com.cedarsoft.serialization.generator.output.serializer

Source Code of com.cedarsoft.serialization.generator.output.serializer.AbstractGenerator

/**
* Copyright (C) cedarsoft GmbH.
*
* Licensed under the GNU General Public License version 3 (the "License")
* with Classpath Exception; you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
*         http://www.cedarsoft.org/gpl3ce
*         (GPL 3 with Classpath Exception)
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3 only, as
* published by the Free Software Foundation. cedarsoft GmbH designates this
* particular file as subject to the "Classpath" exception as provided
* by cedarsoft GmbH in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
* version 3 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 3 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact cedarsoft GmbH, 72810 Gomaringen, Germany,
* or visit www.cedarsoft.com if you need additional information or
* have any questions.
*/

package com.cedarsoft.serialization.generator.output.serializer;

import com.cedarsoft.Version;
import com.cedarsoft.VersionException;
import com.cedarsoft.codegen.CodeGenerator;
import com.cedarsoft.codegen.DecisionCallback;
import com.cedarsoft.codegen.Decorator;
import com.cedarsoft.codegen.NamingSupport;
import com.cedarsoft.codegen.model.DomainObjectDescriptor;
import com.cedarsoft.codegen.model.FieldInitializedInConstructorInfo;
import com.cedarsoft.codegen.model.FieldInitializedInSetterInfo;
import com.cedarsoft.codegen.model.FieldWithInitializationInfo;
import com.cedarsoft.serialization.generator.output.GeneratorBase;
import com.google.common.collect.Maps;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JClassAlreadyExistsException;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JInvocation;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JType;
import com.sun.codemodel.JVar;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
* Generator base class for serializer generators
*
* @param <T> the type of the decision callback
*/
public abstract class AbstractGenerator<T extends DecisionCallback> extends GeneratorBase<T> {
  /**
   * The suffix used for generated serializers
   */

  @Nonnull
  public static final String SERIALIZER_CLASS_NAME_SUFFIX = "Serializer";
  /**
   * The name of the serialize method
   */

  public static final String METHOD_NAME_SERIALIZE = "serialize";
  /**
   * The name of the deserialize method
   */

  public static final String METHOD_NAME_DESERIALIZE = "deserialize";

  public static final String METHOD_NAME_DESERIALIZE_FROM = "deserializeFrom";

  public static final String PARAM_NAME_FORMAT_VERSION = "formatVersion";

  public static final String PARAM_NAME_SERIALIZE_TO = "serializeTo";

  public static final String VAR_NAME_OBJECT = "object";

  protected AbstractGenerator( @Nonnull CodeGenerator codeGenerator ) {
    super( codeGenerator );
  }

  /**
   * Returns the class the serializer extends (including type information!)
   *
   * @param domainType the domain type
   * @return the class the serializer extends
   */
  @Nonnull
  protected abstract JClass createSerializerExtendsExpression( @Nonnull JClass domainType );

  /**
   * Creates the class name for the serializer
   *
   * @param domainClassName the class name of the domain object that is serialized
   * @return the created class name for the serializer
   */
  @Nonnull

  public static String createSerializerClassName( @Nonnull String domainClassName ) {
    return domainClassName + SERIALIZER_CLASS_NAME_SUFFIX;
  }

  /**
   * Generates the serializer
   *
   * @param domainObjectDescriptor the domain object descriptor
   * @return the defined serializer class
   *
   * @throws JClassAlreadyExistsException
   */
  @Nonnull
  public JDefinedClass generateSerializer( @Nonnull DomainObjectDescriptor domainObjectDescriptor ) throws JClassAlreadyExistsException {
    JClass domainType = codeGenerator.ref( domainObjectDescriptor.getQualifiedName() );

    //the class
    JDefinedClass serializerClass = codeModel._class( createSerializerClassName( domainType.fullName() ) )._extends( createSerializerExtendsExpression( domainType ) );

    //the constructor
    JMethod constructor = createConstructor( serializerClass, domainObjectDescriptor );

    JMethod serializeMethod = createSerializeMethodStub( domainType, serializerClass );
    JMethod deserializeMethod = createDeserializeMethodStub( domainType, serializerClass );

    //Add the serialize stuff
    Map<FieldWithInitializationInfo, JVar> fieldToVar = fillDeSerializationMethods( domainObjectDescriptor, serializerClass, serializeMethod, deserializeMethod );

    //Now construct the deserialized object
    constructDeserializedObject( domainObjectDescriptor, deserializeMethod, fieldToVar );

    finishConstructor( serializerClass, constructor );

    return serializerClass;
  }

  protected void finishConstructor( @Nonnull JDefinedClass serializerClass, @Nonnull JMethod constructor ) {
    if ( constructor.listParams().length > 0 ) {
      constructor.body().directStatement( "assert getDelegatesMappings().verify();" );
    }
  }

  protected void constructDeserializedObject( @Nonnull DomainObjectDescriptor domainObjectDescriptor, @Nonnull JMethod deserializeMethod, @Nonnull Map<FieldWithInitializationInfo, JVar> fieldToVar ) {
    deserializeMethod.body().directStatement( "//Constructing the deserialized object" );

    //Now create the constructor for the deserializeMethod
    JClass domainType = codeGenerator.ref( domainObjectDescriptor.getQualifiedName() );
    JInvocation domainTypeInit = JExpr._new( domainType );

    //Add the arguments for the fields
    List<? extends FieldInitializedInConstructorInfo> fieldsToSerialize = domainObjectDescriptor.getFieldsInitializedInConstructor();
    for ( FieldInitializedInConstructorInfo fieldInfo : fieldsToSerialize ) {
      domainTypeInit.arg( fieldToVar.get( fieldInfo ) );
    }

    //Add the object type
    JVar domainObjectVar = deserializeMethod.body().decl( domainType, VAR_NAME_OBJECT, domainTypeInit );

    //Now call the setters
    for ( FieldInitializedInSetterInfo fieldInfo : domainObjectDescriptor.getFieldsInitializedInSetter() ) {
      deserializeMethod.body().add( domainObjectVar.invoke( fieldInfo.getSetterDeclaration().getSimpleName() ).arg( fieldToVar.get( fieldInfo ) ) );
    }

    deserializeMethod.body()._return( domainObjectVar );
  }

  /**
   * Creates the constructor for the given serializer class (if necessary)
   *
   * @param serializerClass        the serialize class
   * @param domainObjectDescriptor the domain object descriptor
   * @return the created constructor
   */
  @Nonnull
  protected abstract JMethod createConstructor( @Nonnull JDefinedClass serializerClass, @Nonnull DomainObjectDescriptor domainObjectDescriptor );

  @Nonnull
  protected JMethod createSerializeMethodStub( @Nonnull JType domainType, @Nonnull JDefinedClass serializerClass ) {
    JMethod serializeMethod = serializerClass.method( JMod.PUBLIC, Void.TYPE, METHOD_NAME_SERIALIZE );
    serializeMethod.annotate( Override.class );
    serializeMethod.param( getSerializeToType(), PARAM_NAME_SERIALIZE_TO );
    serializeMethod.param( domainType, VAR_NAME_OBJECT );
    JVar formatVersion = serializeMethod.param( codeGenerator.ref( Version.class ), PARAM_NAME_FORMAT_VERSION );
    serializeMethod._throws( IOException.class )._throws( ( Class ) getExceptionType() );

    serializeMethod.body().invoke( "verifyVersionWritable" ).arg( formatVersion );

    for ( Decorator decorator : codeGenerator.getDecorators() ) {
      if ( decorator instanceof GeneratorDecorator ) {
        ( ( GeneratorDecorator ) decorator ).decorateSerializeMethod( codeGenerator, domainType, serializerClass, serializeMethod );
      }
    }

    return serializeMethod;
  }

  @Nonnull
  protected JMethod createDeserializeMethodStub( @Nonnull JType domainType, @Nonnull JDefinedClass serializerClass ) {
    JMethod deserializeMethod = serializerClass.method( JMod.PUBLIC, domainType, METHOD_NAME_DESERIALIZE );
    deserializeMethod.param( getSerializeFromType(), METHOD_NAME_DESERIALIZE_FROM );
    JVar formatVersion = deserializeMethod.param( codeGenerator.ref( Version.class ), PARAM_NAME_FORMAT_VERSION );
    deserializeMethod.annotate( Override.class );
    deserializeMethod._throws( IOException.class )._throws( VersionException.class )._throws( ( Class ) getExceptionType() );

    deserializeMethod.body().invoke( "verifyVersionReadable" ).arg( formatVersion );

    for ( Decorator decorator : codeGenerator.getDecorators() ) {
      if ( decorator instanceof GeneratorDecorator ) {
        ( ( GeneratorDecorator ) decorator ).decorateDeserializeMethod( codeGenerator, domainType, serializerClass, deserializeMethod );
      }
    }

    return deserializeMethod;
  }

  @Nonnull
  protected Map<FieldWithInitializationInfo, JVar> fillDeSerializationMethods( @Nonnull DomainObjectDescriptor domainObjectDescriptor, @Nonnull JDefinedClass serializerClass, @Nonnull JMethod serializeMethod, @Nonnull JMethod deserializeMethod ) {
    Map<FieldWithInitializationInfo, JVar> fieldToVar = Maps.newHashMap();

    //Extract the parameters for the serialize method
    JVar serializeTo = serializeMethod.listParams()[0];
    JVar object = serializeMethod.listParams()[1];
    JVar serializeFormatVersion = serializeMethod.listParams()[2];

    //Extract the parameters for the deserialize method
    JVar deserializeFrom = deserializeMethod.listParams()[0];
    JVar deserializeFormatVersion = deserializeMethod.listParams()[1];

    @Nullable JVar wrapper = createDeserializeWrapper( deserializeMethod, deserializeFrom );

    //Generate the serialization and deserialization for every field. We use the ordering of the fields used within the class
    for ( FieldWithInitializationInfo fieldInfo : domainObjectDescriptor.getFieldInfos() ) {
      appendSerializeStatement( serializerClass, serializeMethod, serializeTo, object, serializeFormatVersion, fieldInfo );

      fieldToVar.put( fieldInfo, appendDeserializeStatement( serializerClass, deserializeMethod, deserializeFrom, wrapper, deserializeFormatVersion, fieldInfo ) );
    }

    return fieldToVar;
  }

  /**
   * Creates the deserialize wrapper.
   *
   * This method may be overridden by subclasses if a wrapper shall be used.
   *
   * @param deserializeMethod the deserialize method
   * @param deserializeFrom   the deserialize from
   * @return the wrapper or null if no wrapper exists
   */
  @Nullable
  protected JVar createDeserializeWrapper( @Nonnull JMethod deserializeMethod, @Nonnull JVar deserializeFrom ) {
    return null;
  }

  public void addDelegatingSerializerToConstructor( @Nonnull JDefinedClass serializerClass, @Nonnull JClass fieldType ) {
    JType fieldSerializerType = getSerializerRefFor( fieldType );

    JMethod constructor = ( JMethod ) serializerClass.constructors().next();
    String paramName = NamingSupport.createVarName( fieldSerializerType.name() );

    //Check whether the serializer still exists
    for ( JVar param : constructor.listParams() ) {
      if ( param.type().equals( fieldSerializerType ) ) {
        return;
      }
    }

    //It does not exist, therefore let us add the serializer and map it
    JVar param = constructor.param( fieldSerializerType, paramName );

    constructor.body().add( JExpr.invoke( "add" ).arg( param ).invoke( "responsibleFor" ).arg( JExpr.dotclass( fieldType ) )
                              .invoke( "map" )
                              .arg( JExpr.lit( 1 ) ).arg( JExpr.lit( 0 ) ).arg( JExpr.lit( 0 ) )
                              .invoke( "toDelegateVersion" )
                              .arg( JExpr.lit( 1 ) ).arg( JExpr.lit( 0 ) ).arg( JExpr.lit( 0 ) ) );
  }

  @Nonnull
  protected JClass getSerializerRefFor( @Nonnull JType type ) {
    return codeGenerator.ref( type.fullName() + "Serializer" );
  }

  /**
   * Returns the exception type that is thrown on the serialize and deserialize methods
   *
   * @return the exception type
   */
  @Nonnull
  protected abstract Class<?> getExceptionType();

  /**
   * Returns the type of the serialize from object
   *
   * @return the type of the serialize from object
   */
  @Nonnull
  protected abstract Class<?> getSerializeFromType();

  /**
   * Returns the type of the serializeTo type
   *
   * @return the type of the serializeTo type
   */
  @Nonnull
  protected abstract Class<?> getSerializeToType();

  /**
   * Appends the deserialize statement and return the created var containing the value
   *
   * @param serializerClass   the serializer class
   * @param deserializeMethod the deserialize method
   * @param deserializeFrom   deserialize from
   * @param wrapper           the wrapper that may be used
   * @param formatVersion     the format version
   * @param fieldInfo         the field info
   * @return the var holding the deserialized value
   */
  @Nonnull
  protected abstract JVar appendDeserializeStatement( @Nonnull JDefinedClass serializerClass, @Nonnull JMethod deserializeMethod, @Nonnull JVar deserializeFrom, @Nullable JVar wrapper, @Nonnull JVar formatVersion, @Nonnull FieldWithInitializationInfo fieldInfo );

  /**
   * Appends the serialize statement
   *
   * @param serializerClass the serializer class
   * @param serializeMethod the serialize method
   * @param serializeTo     serialize to
   * @param object          the object that is serialized
   * @param formatVersion   the format version
   * @param fieldInfo       the field info
   */
  protected abstract void appendSerializeStatement( @Nonnull JDefinedClass serializerClass, @Nonnull JMethod serializeMethod, @Nonnull JVar serializeTo, @Nonnull JVar object, @Nonnull JVar formatVersion, @Nonnull FieldWithInitializationInfo fieldInfo );
}
TOP

Related Classes of com.cedarsoft.serialization.generator.output.serializer.AbstractGenerator

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.