package edu.harvard.med.screensaver.ui.arch.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.NoSuchElementException;
import javax.faces.application.Application;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
public class Functions {
/**
* Perform a depth-first traversal of a collection that may contain mixed element types.
* Any nested Iterable instances are visited themselves and their contents added to the
* collection.
*
* @param original an iterable object
* @return a collection containing the results of iterating the original object and any iterable descendents
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Collection flatten(Iterable original) {
Collection flattened = new ArrayList();
if(original == null)
return flattened;
Deque stack = new LinkedList();
stack.push(original.iterator());
while(stack.size() > 0) {
Iterator current = (Iterator)stack.pop();
while(current.hasNext()) {
Object next = current.next();
if(next instanceof Iterable) {
if(next != null) {
stack.push(current);
current = ((Iterable)next).iterator();
}
} else {
flattened.add(next);
}
}
}
return flattened;
}
/**
* Optimised version of distinct(coalesce(flatten(original))).
*
* @param original an iterable object
* @return a collection containing the unique, non-empty results of iterating the original object and any iterable descendents
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Collection compact(Iterable original) {
Collection compacted = new LinkedHashSet();
if(original == null)
return compacted;
Deque stack = new LinkedList();
stack.push(original.iterator());
while(stack.size() > 0) {
Iterator current = (Iterator)stack.pop();
while(current.hasNext()) {
Object next = current.next();
if(next == null
|| "".equals(next)
|| (next instanceof Collection && ((Collection)next).size() == 0)
|| (next instanceof Map && ((Map)next).size() == 0)) {
continue;
} else if(next instanceof Iterable) {
if(next != null) {
stack.push(current);
current = ((Iterable)next).iterator();
}
} else {
compacted.add(next);
}
}
}
return compacted;
}
/**
* Concatenate two iterables
*
* @param first an iterable object
* @param second another iterable object
*
* @return a collection containing the combined results of iterating the two objects
*/
@SuppressWarnings({ "rawtypes" })
public static Iterable concat(final Iterable first, final Iterable second) {
return new Iterable() {
@Override
public Iterator iterator() {
return new Iterator() {
private Iterator firstIter = first.iterator();
private Iterator secondIter = second.iterator();
@Override
public boolean hasNext() {
return (firstIter.hasNext() || secondIter.hasNext());
}
@Override
public Object next() {
if(firstIter.hasNext())
return firstIter.next();
if(secondIter.hasNext())
return secondIter.next();
throw new NoSuchElementException("No items left in either iterator");
}
@Override
public void remove() {
throw new UnsupportedOperationException("Cannot remove from this iterator");
}
};
}
};
}
/**
* Retain the first occurrence of any repeated items in a collection.
*
* @param original an iterable object
* @return a collection containing the unique items in the collection in the order they are returned by the iterator.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Collection distinct(Iterable original) {
Collection flattened = new LinkedHashSet();
if(original == null)
return flattened;
Iterator current = original.iterator();
while(current.hasNext()) {
Object next = current.next();
flattened.add(next);
}
return flattened;
}
/**
* Retain non-empty elements in a collection.
*
* An element is empty if it is null, the empty string, a Collection instance with no elements or
* a Map instance with no mappings.
*
* @param original an iterable object
* @return a collection containing the results of iterating the original object but skipping any empty elements.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Collection coalesce(Iterable original) {
Collection coalesced = new ArrayList();
Iterator current = original.iterator();
while(current.hasNext()) {
Object next = current.next();
if(next == null
|| "".equals(next)
|| (next instanceof Collection && ((Collection)next).size() == 0)
|| (next instanceof Map && ((Map)next).size() == 0)) {
continue;
}
coalesced.add(next);
}
return coalesced;
}
/**
* Evaluate an expression for each element in a collection and return a collection of the evaluation results.
*
* @param original an iterable object
* @param variable a binding to which the element can be assigned
* @param expression a binding (perhaps involving the variable) to return for each element
* @return a collection containing the results
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Collection transform(Iterable original, String variable, String expression) {
FacesContext context = FacesContext.getCurrentInstance();
Application application = context.getApplication();
ValueBinding variableBinding = application.createValueBinding(variable);
ValueBinding expressionBinding = application.createValueBinding(expression);
Collection transformed = new ArrayList();
Iterator current = original.iterator();
while(current.hasNext()) {
Object next = current.next();
variableBinding.setValue(context, next);
transformed.add(expressionBinding.getValue(context));
}
return transformed;
}
/**
* Evaluate an expression for each element in a collection and return a collection containing
* those elements where the expression evaluates to Boolean.TRUE.
*
* @param original an iterable object
* @param variable a binding to which the element can be assigned
* @param expression a binding (perhaps involving the variable) to return for each element
*
* @return a collection containing the elements where the expression evaluates to true.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Collection filter(Iterable original, String variable, String expression) {
FacesContext context = FacesContext.getCurrentInstance();
Application application = context.getApplication();
ValueBinding variableBinding = application.createValueBinding(variable);
ValueBinding expressionBinding = application.createValueBinding(expression);
Collection filtered = new ArrayList();
Iterator current = original.iterator();
while(current.hasNext()) {
Object next = current.next();
variableBinding.setValue(context, next);
if(Boolean.TRUE.equals(expressionBinding.getValue(context)))
filtered.add(next);
}
return filtered;
}
}