/* * Copyright 2008 Fedora Commons, Inc. * * 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 org.mulgara.util.functional; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.Set; /** * Generic Collections utility class. * This class defines static methods for operating on Collections and the functors found in * {@link org.mulgara.util.functional.Fn1} and {@link org.mulgara.util.functional.Fn2}. * * @created Aug 4, 2008 * @author Paula Gearon * @copyright © 2008 <a href="http://www.fedora-commons.org/">Fedora Commons</a> */ public class C { /** * Creates a new list by applying an operator to each element of an initial list. * The final result meets the condition: * <pre>result.get(i) == op( args.get(i) ) for 0 <= i < args.size()</pre> * The operation in op may throw an exception. * @param <T1> The type of the elements in the arg list, which is also the * argument type for the operation. * @param <T2> The type of the elements in the result list, which is also * the return type of the operation. * @param <E> The exception that my be thrown from {@link Fn1E#fn(Object)}. * @param args The input list. * @param op The operation to apply to the elements of the input list. * @return A list whose elements are the result of applying op to each element of args. * @throws E An exception that may be thrown from the {@link Fn1E#fn(Object)} method. */ public static final <T1,T2,E extends Exception> List<T2> map(Collection<T1> args, Fn1E<T1,T2,E> op) throws E { List<T2> result = new LinkedList<T2>(); for (T1 a: args) result.add(op.fn(a)); return result; } /** * The same method as {@link #map(Collection, Fn1E)} for arrays. */ public static final <T1,T2,E extends Exception> List<T2> map(T1[] args, Fn1E<T1,T2,E> op) throws E { List<T2> result = new ArrayList<T2>(args.length); for (T1 a: args) result.add(op.fn(a)); return result; } /** * Creates a new list by applying an operator to each element of an initial list. * The final result meets the condition: * <pre>result.get(i) == op( args.get(i) ) for 0 <= i < args.size()</pre> * The operation in op may <em>not</em> throw an exception. * @param <T1> The type of the elements in the arg list, which is also the * argument type for the operation. * @param <T2> The type of the elements in the result list, which is also * the return type of the operation. * @param args The input list. * @param op The operation to apply to the elements of the input list. * @return A list whose elements are the result of applying op to each element of args. */ public static final <T1,T2> List<T2> map(Collection<T1> args, Fn1<T1,T2> op) { return map(args, (Fn1E<T1,T2,RuntimeException>)op); } /** * The same method as {@link #map(Collection, Fn1)} for arrays. */ public static final <T1,T2> List<T2> map(T1[] args, Fn1<T1,T2> op) { List<T2> result = new ArrayList<T2>(args.length); for (T1 a: args) result.add(op.fn(a)); return result; } /////////////// // List methods /////////////// /** * Returns the head of a linked list. This is a simple wrapper for {@link LinkedList#getFirst()} * @param <T1> The list element type. * @param arg The list. * @return The first element in the list. * @throws NoSuchElementException If the list is empty. */ public static final <T1> T1 head(LinkedList<T1> arg) throws NoSuchElementException { return arg.getFirst(); } /** * Returns the head of a list. * @param <T1> The list element type. * @param arg The list. * @return The first element in the list. * @throws NoSuchElementException If the list is empty. */ public static final <T1> T1 head(List<T1> arg) throws NoSuchElementException { if (arg instanceof LinkedList) return ((LinkedList<T1>)arg).getFirst(); if (arg.size() == 0) throw new NoSuchElementException("Empty list"); return arg.get(0); } /** * Returns the head of a linked list, with a <code>null</code> for an empty list. * @param <T1> The list element type. * @param arg The list. * @return The first element in the list, or <code>null</code> if the list is empty. */ public static final <T1> T1 headN(LinkedList<T1> arg) { return arg.isEmpty() ? null : arg.getFirst(); } /** * Returns the head of a list. * @param <T1> The list element type. * @param arg The list. * @return The first element in the list, or <code>null</code> if the list is empty. */ public static final <T1> T1 headN(List<T1> arg) { return arg.isEmpty() ? null : (arg instanceof LinkedList) ? ((LinkedList<T1>)arg).getFirst() : arg.get(0); } /** * Returns the final elements of a linked list. * This is a simple wrapper for {@link LinkedList#getLast()}. * @param <T1> The list element type. * @param arg The list. * @return The last element in the list. * @throws NoSuchElementException If the list is empty. */ public static final <T1> T1 last(LinkedList<T1> arg) throws NoSuchElementException { return arg.getLast(); } /** * Returns the final element of a list. * @param <T1> The list element type. * @param arg The list. * @return The last element in the list. * @throws IndexOutOfBoundsException If the list is empty. */ public static final <T1> T1 last(List<T1> arg) throws NoSuchElementException { if (arg instanceof LinkedList) return ((LinkedList<T1>)arg).getLast(); if (arg.size() == 0) throw new NoSuchElementException("Empty list"); return arg.get(arg.size() - 1); } /** * Returns the tail of a list. * @param <T1> The list element type. * @param arg The list to get the tail of. * @return A list containing all but the head of arg. */ public static final <T1> List<T1> tail(List<T1> arg) { if (arg.size() == 0) return Collections.emptyList(); return arg.subList(1, arg.size()); } /** * Returns the tail of a list. * @param <T1> The list element type. * @param arg The list. * @return The last element in the list, or <code>null</code> if the list is empty. */ public static final <T1> T1 lastN(LinkedList<T1> arg) { return arg.isEmpty() ? null : arg.getLast(); } /** * Returns the tail of a list. * @param <T1> The list element type. * @param arg The list. * @return The last element in the list, or <code>null</code> if the list is empty. */ public static final <T1> T1 lastN(List<T1> arg) { return arg.isEmpty() ? null : arg.get(arg.size() - 1); } /** * Returns the first item from a Collection. This is a simple wrapper for {@link LinkedList#getFirst()} * @param <T1> The element type. * @param arg The list. * @return The first element in the list. * @throws NoSuchElementException If the list is empty. */ public static final <T1> T1 first(LinkedList<T1> arg) throws NoSuchElementException { return arg.getFirst(); } /** * Returns the first item from an Iterable. * @param <T1> The element type. * @param arg The iterable collection. * @return The first element in the collection. * @throws NoSuchElementException If the collection is empty. */ public static final <T1> T1 first(Collection<T1> arg) throws NoSuchElementException { if (arg instanceof LinkedList) return ((LinkedList<T1>)arg).getFirst(); if (arg.isEmpty()) throw new NoSuchElementException("Empty Collection"); return arg.iterator().next(); } /** * Inserts an element into a list in ascending order. * @param <T> The type of the element to be inserted. Must be comparable on itself. * @param list The list to insert into. This must already be ordered. * @param c The element to insert. * @return The newly modified list with all elements in ascending. */ public static final <T extends Comparable<T>> List<T> ascendingInsert(List<T> list, T c) { return orderedInsert(list, c, true); } /** * Inserts an element into an ordered list in descending order. * @param <T> The type of the element to be inserted. Must be comparable on itself. * @param list The list to insert into. This must already be ordered. * @param c The element to insert. * @return The newly modified list, with all elements in descending order. */ public static final <T extends Comparable<T>> List<T> descendingInsert(List<T> list, T c) { return orderedInsert(list, c, false); } /** * Method to join the elements of a list into a string. * @param <T> The type of element in the list. * @param list The list to be converted to a string. * @param separator The separator to use between elements of the list. May be <code>null</code>. * @return The final string. */ public static final <T> String join(List<T> list, String separator) { return join(list, null, separator, null); } /** * General method to join the elements of a list into a string. * @param <T> The type of element in the list. * @param list The list to be converted to a string. * @param start The start of the string. May be <code>null</code>. * @param separator The separator to use between elements of the list. May be <code>null</code>. * @param end The end of the string. May be <code>null</code>. * @return The final string. */ public static final <T> String join(List<T> list, String start, String separator, String end) { StringBuilder s = start == null ? new StringBuilder() : new StringBuilder(start); boolean first = true; for (T elt: list) { if (!first && separator != null) s.append(separator); else first = false; s.append(elt); } if (end != null) s.append(end); return s.toString(); } /** * Method to create an intersection of a list and an array. The order of the result will * be the same as the order of the original list. * @param list The list to intersect. This will be an ArrayList, no matter the source type. * @param array The array to intersect against the list. * @return A list containing elements that are in both list and array. */ public static final <T> List<T> intersect(List<T> list, T[] array) { HashSet<T> lookup = new HashSet<T>(); for (T e: array) lookup.add(e); List<T> result = new ArrayList<T>(); for (T e: list) if (lookup.contains(e)) result.add(e); return result; } /** * Method to create an intersection of a set and an array. * @param set The set to intersect. This will be a HashSet, no matter the source type. * @param array The array to intersect against the set. * @return A set containing elements that are in both set and array. */ public static final <T> Set<T> intersect(Set<T> set, T[] array) { Set<T> result = new HashSet<T>(); for (T e: array) if (set.contains(e)) result.add(e); return result; } /** * Inserts an element into an ordered list in a given order. * @param <T> The type of the element to be inserted. Must be comparable on itself. * @param list The list to insert into. This must already be ordered in the same order as this insert. * @param c The element to insert. * @return The newly modified list, with all elements in order. */ private static final <T extends Comparable<T>> List<T> orderedInsert(List<T> list, T c, boolean smaller) { ListIterator<T> i = list.listIterator(); while (i.hasNext()) { if (orderTest(c.compareTo(i.next()), smaller)) { i.previous(); break; } } i.add(c); return list; } /** * Tests if a comparison value indicates that smaller or larger values. * @param value The value returned from a comparison. * @param smaller When <code>true</code> this returns true for a comparison indicating a * smaller value was first, otherwise it tests if the comparison indicates * a larger value first. * @return <code>true</code> when the value is in the same direction as the smaller flag indicates. */ private static final boolean orderTest(int value, boolean smaller) { return smaller ? value < 0 : value > 0; } }