package org.qi4j.functional;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
/**
* Utility functions. Combine these with methods in Iterables, for example. See FunctionsTest for usages.
*/
public final class Functions
{
public static <A, B, C> Function2<Function<? super B, C>, Function<A, B>, Function<A, C>> compose()
{
return new Function2<Function<? super B, C>, Function<A, B>, Function<A, C>>()
{
@Override
public Function<A, C> map( Function<? super B, C> bcFunction, Function<A, B> abFunction )
{
return compose( bcFunction, abFunction );
}
};
}
/**
* compose(F1(M,T),F2(F,M)) = F1(F2(F)) -> T
*
* @param outer
* @param inner
* @param <FROM>
* @param <MIDDLE>
* @param <TO>
*
* @return
*/
public static <FROM, MIDDLE, TO> Function<FROM, TO> compose( final Function<? super MIDDLE, TO> outer,
final Function<FROM, MIDDLE> inner
)
{
return new Function<FROM, TO>()
{
@Override
public TO map( FROM from )
{
return outer.map( inner.map( from ) );
}
};
}
public static <TO, FROM extends TO> Function<FROM, TO> identity()
{
return new Function<FROM, TO>()
{
@Override
public TO map( FROM from )
{
return from;
}
};
}
public static <FROM, TO> Function<FROM, TO> fromMap( final Map<FROM, TO> map )
{
return new Function<FROM, TO>()
{
@Override
public TO map( FROM from )
{
return map.get( from );
}
};
}
public static <T> Function<T, T> withDefault( final T defaultValue )
{
return new Function<T, T>()
{
@Override
public T map( T from )
{
if( from == null )
{
return defaultValue;
}
else
{
return from;
}
}
};
}
public static Function<Number, Long> longSum()
{
return new Function<Number, Long>()
{
long sum;
@Override
public Long map( Number number )
{
sum += number.longValue();
return sum;
}
};
}
public static Function<Number, Integer> intSum()
{
return new Function<Number, Integer>()
{
int sum;
@Override
public Integer map( Number number )
{
sum += number.intValue();
return sum;
}
};
}
/**
* Count the number of items in an iterable that matches a given specification.
*
* Sample usage: last( map( count( in( "X" ) ), iterable( "X","Y","X","X","Y" ) ) )
* Returns: 3
*
* @param specification
* @param <T>
*
* @return
*/
public static <T> Function<T, Integer> count( final Specification<T> specification )
{
return new Function<T, Integer>()
{
int count;
@Override
public Integer map( T item )
{
if( specification.satisfiedBy( item ) )
{
count++;
}
return count;
}
};
}
/**
* Find out the index of an item matching a given specification in an iterable.
* Returns -1 if it is not found.
*
* @param specification
* @param <T>
*
* @return
*/
public static <T> Function<T, Integer> indexOf( final Specification<T> specification )
{
return new Function<T, Integer>()
{
int index = -1;
int current = 0;
@Override
public Integer map( T item )
{
if( index == -1 && specification.satisfiedBy( item ) )
{
index = current;
}
current++;
return index;
}
};
}
/**
* Find out the index of an item in an iterable.
*
* @param item
* @param iterable
* @param <T>
*
* @return
*/
public static <T> int indexOf( T item, Iterable<T> iterable )
{
return Iterables.first( Iterables.filter( Specifications.not( Specifications.in( -1 ) ), Iterables.map( indexOf( Specifications
.in( item ) ), iterable ) ) );
}
/**
* Only apply given function on objects that satisfies the given specification.
*
* @param specification
* @param function
* @param <T>
*
* @return
*/
public static <T> Function<T, T> filteredMap( final Specification<T> specification, final Function<T, T> function )
{
return new Function<T, T>()
{
@Override
public T map( T from )
{
return specification.satisfiedBy( from ) ? function.map( from ) : from;
}
};
}
/**
* Creates a comparator that takes a function as input. The returned comparator will use the
* function once for each item in the list to be sorted by Collections.sort.
*
* This should be used if the function to generate the sort key from an object is expensive, so
* that it is not done many times for each item in a list.
*
* @param comparableFunction
* @param <T>
*
* @return
*/
public static <T> Comparator<T> comparator( final Function<T, Comparable> comparableFunction )
{
return new Comparator<T>()
{
Map<T, Comparable> compareKeys = new HashMap<T, Comparable>();
@Override
public int compare( T o1, T o2 )
{
Comparable key1 = compareKeys.get( o1 );
if( key1 == null )
{
key1 = comparableFunction.map( o1 );
compareKeys.put( o1, key1 );
}
Comparable key2 = compareKeys.get( o2 );
if( key2 == null )
{
key2 = comparableFunction.map( o2 );
compareKeys.put( o2, key2 );
}
return key1.compareTo( key2 );
}
};
}
}