Package com.codiform.moo.translator

Source Code of com.codiform.moo.translator.CollectionTranslator

package com.codiform.moo.translator;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

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

import com.codiform.moo.MatcherInitializationException;
import com.codiform.moo.TranslationException;
import com.codiform.moo.UnsupportedTranslationException;
import com.codiform.moo.configuration.Configuration;
import com.codiform.moo.property.CollectionProperty;
import com.codiform.moo.property.source.NoOpSourceProperty;
import com.codiform.moo.property.source.SourceProperty;
import com.codiform.moo.property.source.SourcePropertyFactory;
import com.codiform.moo.session.TranslationSource;

/**
* A translator for handling issues specific to collections. In particular, this handles defensive
* copying of collections that don't need to be transformed, but also deals with making a copy of a
* collection that matches the original collection type (set, list, etc.) but where each item in the
* collection has been translated.
*
* <p>
* This approach has limitations; it can't change the type of the collection, and it can't currently
* handle sorted sets with translation (where does the comparator for the translated objects come
* from?), maps, or allow you to select the implementation class for the collection (LinkedList vs.
* ArrayList).
* </p>
*/
public class CollectionTranslator {

  private Configuration configuration;
  private Logger log;
  private SourcePropertyFactory sourcePropertyFactory;

  /**
   * Create a collection translator with a known configuration.
   *
   * @param configuration
   */
  public CollectionTranslator( Configuration configuration, SourcePropertyFactory sourcePropertyFactory ) {
    this.configuration = configuration;
    this.sourcePropertyFactory = sourcePropertyFactory;
    this.log = LoggerFactory.getLogger( getClass() );
  }

  /**
   * Translate a collection. Make a defensive copy if need be. If the element is annotated with
   * the TranslateCollection element, then create an entirely new collection and translate each
   * element of the contents of the source collection.
   *
   * @param value
   *            the source collection
   * @param property
   *            the {@link CollectionProperty} annotation, if present
   * @param cache
   *            the translation cache of previously-translated elements
   * @return the translated collection
   */
  public Object translate( Object value, CollectionProperty property, TranslationSource cache ) {
    if ( property.shouldItemsBeTranslated() ) {
      Object target = createTargetCollection( value, property, cache );
      translateToTargetCollection( value, target, property, cache );
      return target;
    } else if ( shouldCopy( value, property ) ) {
      Object target = createTargetCollection( value, property, cache );
      copyToTargetCollection( value, target, property );
      return target;
    } else {
      return value;
    }
  }

  private boolean shouldCopy( Object value, CollectionProperty property ) {
    if( !hasDefaultFactory( property ) ) {
      return true;
    }
    if( configuration.isPerformingDefensiveCopies() ) {
      return true;
    }
    if( property.getItemSource() != null ) {
      return true;
    }
    Class<?> targetClass = property.getType();
    if ( !targetClass.isInstance( value ) ) {
      return true;
    }
    return false;
  }

  @SuppressWarnings( "unchecked" )
  private void copyToTargetCollection( Object value, Object target, CollectionProperty property ) {
    if ( value instanceof Collection ) {
      if ( target instanceof Collection ) {
        Collection<Object> targetCollection = (Collection<Object>)target;
        Iterator<?> sourceItems = ( (Collection<?>)value ).iterator();
        SourceProperty itemSource = getItemSource( property.getItemSource() );
        while ( sourceItems.hasNext() ) {
          Object item = itemSource.getValue( sourceItems.next() );
          targetCollection.add( item );
        }
      } else {
        throw new TranslationException( "Cannot translate collection to target of type: " + target.getClass().getName() );
      }
    } else {
      throw new TranslationException( "Cannot translate collection from type: " + value.getClass().getName() );
    }
  }

  private boolean hasDefaultFactory( CollectionProperty property ) {
    return property.getFactory() == DefaultCollectionTargetFactory.class;
  }

  @SuppressWarnings( "unchecked" )
  private void translateToTargetCollection( Object value, Object target, CollectionProperty property, TranslationSource cache ) {
    if ( value instanceof Collection ) {
      if ( target instanceof Collection ) {
        Collection<Object> targetCollection = (Collection<Object>)target;
        Iterator<?> sourceItems = ( (Collection<?>)value ).iterator();
        SourceProperty itemSource = getItemSource( property.getItemSource() );
        while ( sourceItems.hasNext() ) {
          Object item = itemSource.getValue( sourceItems.next() );
          Object translated = cache.getTranslation( item, property.getItemClass() );
          targetCollection.add( translated );
        }
      } else {
        throw new TranslationException( "Cannot translate collection to target of type: " + target.getClass().getName() );
      }
    } else {
      throw new TranslationException( "Cannot translate from collection of type: " + target.getClass().getName() );
    }
  }

  private SourceProperty getItemSource( String itemExpression ) {
    if( itemExpression == null )
      return new NoOpSourceProperty();
    else
      return sourcePropertyFactory.getSourceProperty( itemExpression );
  }

  private Object createTargetCollection( Object value, CollectionProperty property, TranslationSource cache ) {
    Class<? extends TranslationTargetFactory> factoryType = property.getFactory();
    TranslationTargetFactory factory = cache.getTranslationTargetFactory( factoryType );
    Object targetCollection = factory.getTranslationTargetInstance( value, property.getType() );
    log.trace( "Target factory type {} created target collection of type {} for source {}", factoryType, targetCollection.getClass(), value );
    return targetCollection;
  }

  @SuppressWarnings( "unchecked" )
  public void updateCollection( Object source, Collection<Object> destinationCollection, TranslationSource translationSource,
      CollectionProperty property ) {
    if ( source instanceof Collection ) {
      Collection<Object> sourceCollection = (Collection<Object>)source;
      if ( property.hasMatcher() ) {
        updateCollectionWithMatcher( sourceCollection, destinationCollection, translationSource, property );
      } else {
        updateCollectionInOrder( sourceCollection, destinationCollection, translationSource, property );
      }
    } else {
      throw new UnsupportedTranslationException( "Cannot update Collection from " + source.getClass().getName() );
    }
  }

  private void updateCollectionWithMatcher( Collection<Object> sourceCollection, Collection<Object> destinationCollection,
      TranslationSource translationSource, CollectionProperty property ) {
    Class<CollectionMatcher<Object, Object>> matcherClass = property.getMatcherType();
    try {
      Collection<Object> unmatched = new ArrayList<Object>( destinationCollection );
      CollectionMatcher<Object, Object> matcher = matcherClass.newInstance();
      matcher.setTargets( destinationCollection );
      for ( Object source : sourceCollection ) {
        Object destination = matcher.getTarget( source );
        if ( destination == null ) {
          if ( property.shouldItemsBeTranslated() ) {
            destinationCollection.add( translationSource.getTranslation( source, property.getItemClass() ) );
          } else {
            destinationCollection.add( source );
          }
        } else {
          unmatched.remove( destination );
          translationSource.update( source, destination );
        }
      }
      if ( property.shouldRemoveOrphans() ) {
        for ( Object item : unmatched ) {
          destinationCollection.remove( item );
        }
      }
    } catch ( InstantiationException exception ) {
      throw new MatcherInitializationException( matcherClass, exception );
    } catch ( IllegalAccessException exception ) {
      throw new MatcherInitializationException( matcherClass, exception );
    }
  }

  private void updateCollectionInOrder( Collection<Object> sourceCollection, Collection<Object> destinationCollection,
      TranslationSource translationSource, CollectionProperty property ) {
    Iterator<Object> source = sourceCollection.iterator();
    Iterator<Object> destination = destinationCollection.iterator();

    while ( source.hasNext() && destination.hasNext() ) {
      translationSource.update( source.next(), destination.next() );
    }

    if ( source.hasNext() && !destination.hasNext() ) {
      while ( source.hasNext() ) {
        if ( property.shouldItemsBeTranslated() ) {
          Object translation = translationSource.getTranslation( source.next(), property.getItemClass() );
          destinationCollection.add( translation );
        } else {
          destinationCollection.add( source.next() );
        }
      }
    } else if ( destination.hasNext() && !source.hasNext() && property.shouldRemoveOrphans() ) {
      while ( destination.hasNext() ) {
        destination.next();
        destination.remove();
      }
    }
  }

}
TOP

Related Classes of com.codiform.moo.translator.CollectionTranslator

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.