Package ma.glasnost.orika.impl.generator

Source Code of ma.glasnost.orika.impl.generator.MapperGenerator

/*
* Orika - simpler, better and faster Java bean mapping
*
* Copyright (C) 2011 Orika authors
*
* 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 ma.glasnost.orika.impl.generator;

import static java.lang.String.format;
import static ma.glasnost.orika.impl.generator.SourceCodeContext.append;

import java.util.LinkedHashSet;
import java.util.Set;

import javassist.CannotCompileException;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.MappingContext;
import ma.glasnost.orika.MappingException;
import ma.glasnost.orika.impl.GeneratedMapperBase;
import ma.glasnost.orika.metadata.ClassMap;
import ma.glasnost.orika.metadata.FieldMap;
import ma.glasnost.orika.metadata.MapperKey;
import ma.glasnost.orika.metadata.Type;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public final class MapperGenerator {
   
    private static Logger LOGGER = LoggerFactory.getLogger(MapperGenerator.class);
   
    private final MapperFactory mapperFactory;
    private final CompilerStrategy compilerStrategy;
   
    public MapperGenerator(MapperFactory mapperFactory, CompilerStrategy compilerStrategy) {
        this.mapperFactory = mapperFactory;
        this.compilerStrategy = compilerStrategy;
    }
   
    public GeneratedMapperBase build(ClassMap<?, ?> classMap, MappingContext context) {
       
        try {
            compilerStrategy.assureTypeIsAccessible(classMap.getAType().getRawType());
            compilerStrategy.assureTypeIsAccessible(classMap.getBType().getRawType());
           
           
            StringBuilder logDetails;
            if (LOGGER.isDebugEnabled()) {
              logDetails = new StringBuilder();
              logDetails.append("Generating new mapper for (" + classMap.getAType()+", " + classMap.getBTypeName() +")");
            } else {
              logDetails = null;
            }
           
            final SourceCodeContext mapperCode = new SourceCodeContext(
                    classMap.getMapperClassName(), GeneratedMapperBase.class, context, logDetails);
           
            Set<FieldMap> mappedFields = new LinkedHashSet<FieldMap>();
            mappedFields.addAll(addMapMethod(mapperCode, true, classMap, logDetails));
            mappedFields.addAll(addMapMethod(mapperCode, false, classMap, logDetails));
           
            GeneratedMapperBase instance = mapperCode.getInstance();
            instance.setAType(classMap.getAType());
            instance.setBType(classMap.getBType());
           
           
            if (logDetails != null) {
              LOGGER.debug(logDetails.toString());
            }
           
            /*
             * Add a copy of the ClassMap to the current mapping context, which only contains the field
             * maps that were processed by this mapper generation; this can later be used by ObjectFactory
             * generation when selecting a constructor -- since we only need a constructor which handles
             * the fields not mapped by the generated mapper
             */
            classMap = classMap.copy(mappedFields);
            context.registerMapperGeneration(classMap);
           
            return instance;
           
        } catch (final Exception e) {
            throw new MappingException(e);
        }
    }
   
    private String getFieldTag(FieldMap fieldMap) {
      return "\n\t Field(" + fieldMap.getSource() + ", " + fieldMap.getDestination() + ") : ";
    }
   
    private Set<FieldMap> addMapMethod(SourceCodeContext code, boolean aToB, ClassMap<?, ?> classMap, StringBuilder logDetails) throws CannotCompileException {
       
       
        Set<FieldMap> mappedFields = new LinkedHashSet<FieldMap>();
      if (logDetails != null) {
          if (aToB) {
            logDetails.append("\n\t" +code.getClassSimpleName() + ".mapAToB("+ classMap.getAType()+", " + classMap.getBTypeName() +") {");
          } else {
            logDetails.append("\n\t" +code.getClassSimpleName() + ".mapBToA("+ classMap.getBType()+", " + classMap.getATypeName() +") {");
          }
        }
     
      final StringBuilder out = new StringBuilder();
        final String mapMethod = "map" + (aToB ? "AtoB" : "BtoA");
        out.append("\tpublic void ");
        out.append(mapMethod);
        out.append(format("(java.lang.Object a, java.lang.Object b, %s mappingContext) {\n\n", MappingContext.class.getCanonicalName()));
       
        VariableRef source;
        VariableRef destination;
        if (aToB) {
            source = new VariableRef(classMap.getAType(), "source");
            destination = new VariableRef(classMap.getBType(), "destination");
        } else {
            source = new VariableRef(classMap.getBType(), "source");
            destination = new VariableRef(classMap.getAType(), "destination");
        }
        
        append(out,
                format("super.%s(a, b, mappingContext);", mapMethod),
                "\n\n",
                source.declare("a"),
                destination.declare("b"),
                "\n\n");
       
        for (FieldMap currentFieldMap : classMap.getFieldsMapping()) {
           
         
            if (currentFieldMap.isExcluded()) {
              if (logDetails != null) {
                logDetails.append(getFieldTag(currentFieldMap) + "excuding (explicitly)");
              }
                continue;
            }
           
            if (isAlreadyExistsInUsedMappers(currentFieldMap, classMap)) {
              if (logDetails != null) {
                logDetails.append(getFieldTag(currentFieldMap) + "excluding because it is already handled by another mapper in this hierarchy");
               
              }
              continue;
            }
           
            FieldMap fieldMap = currentFieldMap;
            if (!aToB) {
                fieldMap = fieldMap.flip();
            }
           
            if (code.aggregateSpecsApply(fieldMap)) {
                continue;
            }
           
            if (logDetails != null) {
            logDetails.append(getFieldTag(fieldMap));
          }
           
            if (!fieldMap.isIgnored()) {
                try {
                    mappedFields.add(currentFieldMap);
                    String sourceCode = generateFieldMapCode(code, fieldMap, classMap, destination, logDetails);
                    out.append(sourceCode);
                } catch (final Exception e) {
                    MappingException me = new MappingException(e);
                    me.setSourceProperty(fieldMap.getSource());
                    me.setDestinationProperty(fieldMap.getDestination());
                    me.setSourceType(source.type());
                    me.setDestinationType(destination.type());
                    throw me;
                }
            } else if (logDetails !=null) {
              logDetails.append("ignored for this mapping direction");
            }
        }
       
        out.append(code.mapAggregateFields());
               
        out.append("\n\t\tif(customMapper != null) { \n\t\t\t customMapper.")
                .append(mapMethod)
                .append("(source, destination, mappingContext);\n\t\t}");
       
        out.append("\n\t}");
       
        if (logDetails != null) {
          logDetails.append("\n\t}");
        }
       
        code.addMethod(out.toString());
       
        return mappedFields;
    }
   
    private boolean isAlreadyExistsInUsedMappers(FieldMap fieldMap, ClassMap<?, ?> classMap) {
       
        Set<ClassMap<Object, Object>> usedClassMapSet = mapperFactory.lookupUsedClassMap(new MapperKey(classMap.getAType(),
                classMap.getBType()));
       
        if (!fieldMap.isByDefault()) {
          return false;
        }
       
        for (ClassMap<Object, Object> usedClassMap : usedClassMapSet) {
            for(FieldMap usedFieldMap: usedClassMap.getFieldsMapping()) {
              if (usedFieldMap.getSource().equals(fieldMap.getSource())
                  && usedFieldMap.getDestination().equals(fieldMap.getDestination())) {
                return true;
              }
            }
        }
       
        return false;
    }
   
    private String generateFieldMapCode(SourceCodeContext code, FieldMap fieldMap, ClassMap<?, ?> classMap, VariableRef destination, StringBuilder logDetails) throws Exception {
       
        final VariableRef sourceProperty = new VariableRef(fieldMap.getSource(), "source");
        final VariableRef destinationProperty = new VariableRef(fieldMap.getDestination(), "destination");
        destinationProperty.setOwner(destination);
       
        if (!sourceProperty.isReadable() || ((!destinationProperty.isAssignable()) && !destinationProperty.isCollection() && !destinationProperty.isArray() && !destinationProperty.isMap())) {
            if (logDetails != null) {
               
              logDetails.append("excluding because ");
          if (!sourceProperty.isReadable()) {
              Type<?> sourceType = classMap.getAType().equals(destination.type()) ? classMap.getBType() : classMap.getAType();
            logDetails.append(sourceType + "." + fieldMap.getSource().getName() + "(" + fieldMap.getSource().getType() + ") is not readable");
          } else {
            // TODO: this brings up an important case: sometimes the destination is not assignable,
            // but it's properties can still be mapped in-place. Should we handle it?
             
            logDetails.append(destination.type() + "." + fieldMap.getSource().getName() + "(" + fieldMap.getSource().getType() + ") is neither assignable nor an array, collection, or map");
          }   
            }
          return "";
        }
       
        // Make sure the source and destination types are accessible to the builder
        compilerStrategy.assureTypeIsAccessible(sourceProperty.rawType());
        compilerStrategy.assureTypeIsAccessible(destinationProperty.rawType());

        return code.mapFields(fieldMap, sourceProperty, destinationProperty, destination.type(), logDetails);
    }
   
}
TOP

Related Classes of ma.glasnost.orika.impl.generator.MapperGenerator

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.