Package com.inspiresoftware.lib.dto.geda.assembler

Source Code of com.inspiresoftware.lib.dto.geda.assembler.DTOtoEntityAssemblerImpl

/*
* This code is distributed under The GNU Lesser General Public License (LGPLv3)
* Please visit GNU site for LGPLv3 http://www.gnu.org/copyleft/lesser.html
*
* Copyright Denis Pavlov 2009
* Web: http://www.genericdtoassembler.org
* SVN: https://svn.code.sf.net/p/geda-genericdto/code/trunk/
* SVN (mirror): http://geda-genericdto.googlecode.com/svn/trunk/
*/


package com.inspiresoftware.lib.dto.geda.assembler;

import com.inspiresoftware.lib.dto.geda.adapter.BeanFactory;
import com.inspiresoftware.lib.dto.geda.assembler.extension.Configurable;
import com.inspiresoftware.lib.dto.geda.assembler.extension.MethodSynthesizer;
import com.inspiresoftware.lib.dto.geda.assembler.meta.CollectionPipeMetadata;
import com.inspiresoftware.lib.dto.geda.assembler.meta.FieldPipeMetadata;
import com.inspiresoftware.lib.dto.geda.assembler.meta.MapPipeMetadata;
import com.inspiresoftware.lib.dto.geda.assembler.meta.PipeMetadata;
import com.inspiresoftware.lib.dto.geda.dsl.Registries;
import com.inspiresoftware.lib.dto.geda.dsl.Registry;
import com.inspiresoftware.lib.dto.geda.exception.*;

import java.beans.PropertyDescriptor;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import java.util.*;


/**
* Assemble DTO and Entities depending on the annotations of Dto.
*
* @author Denis Pavlov
* @since 2.0.2
*
*/
@SuppressWarnings("unchecked")
public final class DTOtoEntityAssemblerImpl implements Assembler, AssemblerContext, Configurable {

    private static final MetadataChainBuilder ANNOTATIONS = new MetadataChainAnnotationBuilder();

    private static final PipeBuilder<FieldPipeMetadata> FIELD = new DataPipeBuilder();
    private static final PipeBuilder<FieldPipeMetadata> VIRTUAL = new DataVirtualPipeBuilder();
    private static final PipeBuilder<CollectionPipeMetadata> COLLECTION = new CollectionPipeBuilder();
    private static final PipeBuilder<MapPipeMetadata> MAP = new MapPipeBuilder();
    private static final PipeBuilder<PipeMetadata> CHAIN = new DataPipeChainBuilder();


    private final Class dtoClass;
  private final Class entityClass;
  private final MethodSynthesizer synthesizer;
    private final Registry dslRegistry;

    private final Reference<ClassLoader> classLoader;

    private Pipe[] pipes;

  DTOtoEntityAssemblerImpl(final Class dto,
                             final Class entity,
                             final ClassLoader classLoader,
                             final MethodSynthesizer synthesizer,
                             final Registry registry)
    throws InspectionScanningException, UnableToCreateInstanceException, InspectionPropertyNotFoundException,
           InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
           GeDARuntimeException, AnnotationDuplicateBindingException {

        this(dto, entity, classLoader, synthesizer, registry, true);
    }

  DTOtoEntityAssemblerImpl(final Class dto, final Class entity,
                             final ClassLoader classLoader,
                             final MethodSynthesizer synthesizer,
                             final Registry registry,
                             final boolean strict)
    throws InspectionScanningException, UnableToCreateInstanceException, InspectionPropertyNotFoundException,
           InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
           GeDARuntimeException, AnnotationDuplicateBindingException {
    dtoClass = dto;
    entityClass = entity;
    this.synthesizer = synthesizer;
        this.classLoader = new SoftReference<ClassLoader>(classLoader);
        dslRegistry = registry;

        final MetadataChainBuilder metaBuilder;
        if (registry == null) {
            metaBuilder = ANNOTATIONS;
        } else {
            metaBuilder = new MetadataChainDSLBuilder(registry, dtoClass, entityClass);
        }

    Class dtoMap = dto;
        final LinkedList pipes = new LinkedList();
        while (dtoMap != null) { // when we reach Object.class this should be null

      mapRelationMapping(dtoMap, entity, strict, pipes, metaBuilder);
            final Type type = dtoMap.getGenericSuperclass();
            if (type != null) {
                dtoMap = PropertyInspector.getClassForType(type);
            } else {
                dtoMap = null;
            }

    }
        this.pipes = (Pipe[]) pipes.toArray(new Pipe[pipes.size()]);

    }

  private void mapRelationMapping(final Class dto, final Class entity, final boolean strict, final List<Pipe> pipes, final MetadataChainBuilder metaBuilder)
    throws InspectionScanningException, UnableToCreateInstanceException, InspectionPropertyNotFoundException,
           InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
           GeDARuntimeException, AnnotationDuplicateBindingException {

        final boolean isMapOrListEntity = Map.class.isAssignableFrom(entity) || List.class.isAssignableFrom(entity);

        final PropertyDescriptor[] dtoPropertyDescriptors =
      PropertyInspector.getPropertyDescriptorsForClass(dto);
    final PropertyDescriptor[] entityPropertyDescriptors;
        if (isMapOrListEntity) {
            entityPropertyDescriptors = null;
        } else {
            entityPropertyDescriptors = PropertyInspector.getPropertyDescriptorsForClass(entity);
        }

        final Set<String> bindings = new TreeSet<String>();
        final Field[] dtoFields = dto.getDeclaredFields();
    for (Field dtoField : dtoFields) {

      final List<PipeMetadata> metas = metaBuilder.build(dtoField);
      if (metas == null || metas.isEmpty()) {
        continue;
      }
            try {
          final Pipe pipe = createPipeChain(dtoClass, dtoPropertyDescriptors,
                                                  entityClass, entityPropertyDescriptors,
                                                  dtoField, metas, 0, isMapOrListEntity);
                final String binding = pipe.getBinding();

                if (bindings.contains(binding)) {
                    throw new AnnotationDuplicateBindingException(dtoClass.getCanonicalName(), binding);
                }
                bindings.add(binding);
                pipes.add(pipe);
            } catch (InspectionBindingNotFoundException noField) {
                if (strict) { // only throw exception if we are in strict mode
                    throw noField;
                }
            }
    }
  }

  private Pipe createPipeChain(
            final Class dto, final PropertyDescriptor[] dtoPropertyDescriptors,
            final Class entity, final PropertyDescriptor[] entityPropertyDescriptors,
            final Field dtoField, final List<PipeMetadata> metas, final int index, final boolean isMapOrListEntity)
    throws InspectionPropertyNotFoundException, InspectionBindingNotFoundException, InspectionScanningException,
         UnableToCreateInstanceException, AnnotationMissingBindingException, AnnotationValidatingBindingException, GeDARuntimeException {

    final PipeMetadata meta = metas.get(index);

        if (index + 1 == metas.size() || isMapOrListEntity) {
            // build actual pipe for last in chain, maps or lists
            // (since maps and lists do not have any nested properties, maybe another feature?)
      if (meta instanceof FieldPipeMetadata) {
        if (meta.getEntityFieldName().startsWith("#this#")) {
          // create virtual field pipe
          return VIRTUAL.build(this,
              dto, entity, dtoPropertyDescriptors, entityPropertyDescriptors,
                            (FieldPipeMetadata) meta, null);
        } else {
          // create field pipe
          return FIELD.build(this,
              dto, entity, dtoPropertyDescriptors, entityPropertyDescriptors,
                            (FieldPipeMetadata) meta, null);
        }
      } else if (meta instanceof CollectionPipeMetadata) {
        // create collection
        return COLLECTION.build(this,
            dto, entity, dtoPropertyDescriptors, entityPropertyDescriptors,
                        (CollectionPipeMetadata) meta, null);
      } else if (meta instanceof MapPipeMetadata) {
        // create map
        return MAP.build(this,
            dto, entity, dtoPropertyDescriptors, entityPropertyDescriptors,
                        (MapPipeMetadata) meta, null);
      } else {
        throw new GeDARuntimeException("Unknown pipe meta: " + meta.getClass());
      }
    }

    final PropertyDescriptor nested = PropertyInspector.getEntityPropertyDescriptorForField(
        dto, entity, meta.getDtoFieldName(), meta.getEntityFieldName(), entityPropertyDescriptors);
    final PropertyDescriptor[] nestedEntityPropertyDescriptors = PropertyInspector.getPropertyDescriptorsForClassReturnedByGet(nested);

    // build a chain pipe
    return CHAIN.build(this, dto, entity, dtoPropertyDescriptors, entityPropertyDescriptors, meta,
        createPipeChain(dto, dtoPropertyDescriptors, entity, nestedEntityPropertyDescriptors, dtoField, metas, index + 1, isMapOrListEntity)
      );

  }

  /**
   * Configure synthesizer.
   *
   * @param configuration configuration name
   * @param value value to set
   * @return true if configuration was set, false if not set or invalid
   * @throws com.inspiresoftware.lib.dto.geda.exception.GeDAException in case there are errors
   */
  public boolean configure(final String configuration, final Object value) throws GeDAException {
    return this.synthesizer.configure(configuration, value);
  }

    private BeanFactory resolveBeanFactory(BeanFactory beanFactory) {
        if (beanFactory == null) {
            if (dslRegistry != null) {
                return Registries.beanFactory(dslRegistry);
            }
        }
        return beanFactory;
    }

  /** {@inheritDoc} */
  public void assembleDto(final Object dto, final Object entity,
      final Map<String, Object> converters,
      final BeanFactory dtoBeanFactory)
    throws InspectionInvalidDtoInstanceException, InspectionInvalidEntityInstanceException, BeanFactoryNotFoundException,
           BeanFactoryUnableToCreateInstanceException, AnnotationMissingException, NotValueConverterException,
           ValueConverterNotFoundException, UnableToCreateInstanceException, CollectionEntityGenericReturnTypeException,
           InspectionScanningException, InspectionPropertyNotFoundException, InspectionBindingNotFoundException,
           AnnotationMissingBindingException, AnnotationValidatingBindingException, GeDARuntimeException,
           AnnotationDuplicateBindingException {

    validateDtoAndEntity(dto, entity);

    for (Pipe pipe : pipes) {
      pipe.writeFromEntityToDto(entity, dto, converters, resolveBeanFactory(dtoBeanFactory));
    }

  }

    /** {@inheritDoc} */
  public void assembleDtos(final Collection dtos, final Collection entities,
                            final Map<String, Object> converters,
                            final BeanFactory dtoBeanFactory)
    throws InvalidDtoCollectionException, UnableToCreateInstanceException, InspectionInvalidDtoInstanceException,
           InspectionInvalidEntityInstanceException, BeanFactoryNotFoundException, BeanFactoryUnableToCreateInstanceException,
           AnnotationMissingException, NotValueConverterException, ValueConverterNotFoundException,
           CollectionEntityGenericReturnTypeException, InspectionScanningException, InspectionPropertyNotFoundException,
           InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
           GeDARuntimeException, AnnotationDuplicateBindingException {

    if (dtos instanceof Collection && dtos.isEmpty() && entities instanceof Collection) {

            final BeanFactory beanFactory = resolveBeanFactory(dtoBeanFactory);
      for (Object entity : entities) {
        try {
          final Object dto = this.dtoClass.newInstance();
          assembleDto(dto, entity, converters, beanFactory);
          dtos.add(dto);
        } catch (InstantiationException exp) {
          throw new UnableToCreateInstanceException(this.dtoClass.getCanonicalName(),
              "Unable to create dto instance for: " + this.dtoClass.getName(), exp);
        } catch (IllegalAccessException exp) {
          throw new UnableToCreateInstanceException(this.dtoClass.getCanonicalName(),
              "Unable to create dto instance for: " + this.dtoClass.getName(), exp);
        }
      }

    } else {
      throw new InvalidDtoCollectionException();
    }

  }

    /** {@inheritDoc} */
  public void assembleEntity(final Object dto, final Object entity,
      final Map<String, Object> converters, final BeanFactory entityBeanFactory)
    throws InspectionInvalidDtoInstanceException, InspectionInvalidEntityInstanceException, BeanFactoryNotFoundException,
           BeanFactoryUnableToCreateInstanceException, NotEntityRetrieverException, EntityRetrieverNotFoundException,
           NotValueConverterException, ValueConverterNotFoundException, AnnotationMissingBeanKeyException,
           AnnotationMissingException, UnableToCreateInstanceException, CollectionEntityGenericReturnTypeException,
           InspectionScanningException, InspectionPropertyNotFoundException, InspectionBindingNotFoundException,
           AnnotationMissingBindingException, AnnotationValidatingBindingException, GeDARuntimeException,
           AnnotationDuplicateBindingException, DtoToEntityMatcherNotFoundException, NotDtoToEntityMatcherException {

    validateDtoAndEntity(dto, entity);

    for (Pipe pipe : pipes) {
      pipe.writeFromDtoToEntity(entity, dto, converters, resolveBeanFactory(entityBeanFactory));
    }

  }

    /** {@inheritDoc} */
  public void assembleEntities(final Collection dtos, final Collection entities,
      final Map<String, Object> converters, final BeanFactory entityBeanFactory)
    throws UnableToCreateInstanceException, InvalidEntityCollectionException, InspectionInvalidDtoInstanceException,
           InspectionInvalidEntityInstanceException, BeanFactoryNotFoundException, BeanFactoryUnableToCreateInstanceException,
           NotEntityRetrieverException, EntityRetrieverNotFoundException, NotValueConverterException,
           ValueConverterNotFoundException, AnnotationMissingBeanKeyException, AnnotationMissingException,
           CollectionEntityGenericReturnTypeException, InspectionScanningException, InspectionPropertyNotFoundException,
           InspectionBindingNotFoundException, AnnotationMissingBindingException, AnnotationValidatingBindingException,
           GeDARuntimeException, AnnotationDuplicateBindingException, DtoToEntityMatcherNotFoundException,
           NotDtoToEntityMatcherException {

    if (dtos instanceof Collection && entities instanceof Collection && entities.isEmpty()) {

            final BeanFactory beanFactory = resolveBeanFactory(entityBeanFactory);
      for (Object dto : dtos) {
        try {
          final Object entity = this.entityClass.newInstance();
          assembleEntity(dto, entity, converters, beanFactory);
          entities.add(entity);
        } catch (InstantiationException exp) {
          throw new UnableToCreateInstanceException(this.dtoClass.getCanonicalName(),
              "Unable to create entity instance for: " + this.dtoClass.getName(), exp);
        } catch (IllegalAccessException exp) {
          throw new UnableToCreateInstanceException(this.dtoClass.getCanonicalName(),
              "Unable to create entity instance for: " + this.dtoClass.getName(), exp);
        }
      }

    } else {
      throw new InvalidEntityCollectionException();
    }

  }


  private void validateDtoAndEntity(final Object dto, final Object entity)
      throws InspectionInvalidDtoInstanceException, InspectionInvalidEntityInstanceException {
    if (!this.dtoClass.isInstance(dto)) {
      throw new InspectionInvalidDtoInstanceException(this.dtoClass.getCanonicalName(), dto);
    }
    if (!this.entityClass.isInstance(entity)) {
      throw new InspectionInvalidEntityInstanceException(this.entityClass.getCanonicalName(), entity);
    }
  }

    /** {@inheritDoc} */
    public Assembler newAssembler(final Class<?> dto, final Class<?> entity) {
        if (dslRegistry == null) {
            return DTOAssembler.newCustomAssembler(dto, entity, getClassLoader(), synthesizer);
        }
        return  DTOAssembler.newCustomAssembler(dto, entity, getClassLoader(), dslRegistry, synthesizer);
    }

    /** {@inheritDoc} */
    public MethodSynthesizer getMethodSynthesizer() {
        return synthesizer;
    }

    /** {@inheritDoc} */
    public Registry getDslRegistry() {
        return dslRegistry;
    }

    /** {@inheritDoc} */
    public ClassLoader getClassLoader() throws GeDARuntimeException {
        ClassLoader cl = classLoader.get();
        if (cl == null) { // Cl was garbage collected - something gone really wrong
            throw new GeDARuntimeException("Class loader has been gc'ed");
        }
        return cl;
    }

    /** {@inheritDoc} */
    public void releaseResources() {
        synthesizer.releaseResources();
        if (dslRegistry != null) {
            dslRegistry.releaseResources();
        }
    }
}
TOP

Related Classes of com.inspiresoftware.lib.dto.geda.assembler.DTOtoEntityAssemblerImpl

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.