001/* Copyright (C) 2013 TU Dortmund
002 * This file is part of AutomataLib, http://www.automatalib.net/.
003 * 
004 * AutomataLib is free software; you can redistribute it and/or
005 * modify it under the terms of the GNU Lesser General Public
006 * License version 3.0 as published by the Free Software Foundation.
007 * 
008 * AutomataLib is distributed in the hope that it will be useful,
009 * but WITHOUT ANY WARRANTY; without even the implied warranty of
010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
011 * Lesser General Public License for more details.
012 * 
013 * You should have received a copy of the GNU Lesser General Public
014 * License along with AutomataLib; if not, see
015 * http://www.gnu.de/documents/lgpl.en.html.
016 */
017package net.automatalib.commons.util.collections;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.Iterator;
023import java.util.List;
024import java.util.NoSuchElementException;
025import java.util.RandomAccess;
026
027/**
028 * Various methods for operating on collections.
029 * 
030 * @author Malte Isberner <malte.isberner@gmail.com>
031 *
032 */
033public abstract class CollectionsUtil {
034        
035        /*
036         * Prevent inheritance.
037         */
038        private CollectionsUtil() {
039        }
040        
041        /**
042         * Adds all elements from an iterable to a collection.
043         * @param <E> element class.
044         * @param coll the collection to which the elements are added.
045         * @param it the iterable providing the elements to add.
046         * @return <code>true</code> if the collection was modified,
047         * <code>false</code> otherwise.
048         */
049        public static <E> boolean addAll(Collection<E> coll, Iterable<? extends E> it) {
050                boolean modified = false;
051                for(E e : it)
052                        modified = coll.add(e) || modified;
053                return modified;
054        }
055        
056        /**
057         * Retrieves an unmodifiable list only containing null values
058         * of the given size.
059         * @param size the size
060         * @return a list consisting of the specified number of <tt>null</tt> values
061         */
062        @SuppressWarnings("unchecked")
063        public static <E> List<E> nullList(int size) {
064                return (List<E>)(List<?>)new NullList(size);
065        }
066        
067        public static <E> E removeReplace(List<E> list, int index) {
068                int lastIdx = list.size() - 1;
069                E last = list.remove(lastIdx);
070                if(lastIdx != index) {
071                        list.set(index, last);
072                        return last;
073                }
074                return null;
075        }
076        
077        public static List<Integer> intRange(int start, int end) {
078                return new IntRange(start, end);
079        }
080        
081        public static List<Integer> intRange(int start, int step, int end) {
082                return new IntRange(start, step, end);
083        }
084        
085        public static List<Character> charRange(char start, char end) {
086                return new CharRange(start, end);
087        }
088        
089        public static <T> List<? extends T> randomAccessList(Collection<? extends T> coll) {
090                if(coll instanceof List && coll instanceof RandomAccess)
091                        return (List<? extends T>)coll;
092                return new ArrayList<>(coll);
093        }
094        
095        
096        public static <T> Iterable<List<T>> allTuples(final Iterable<T> domain, final int minLength, final int maxLength) {
097                // Check if domain is empty
098                // If it is, then the empty tuple (if not excluded by minLength > 0) is still part of the result
099                // Otherwise, the result is empty
100                if(!domain.iterator().hasNext()) {
101                        if(minLength == 0)
102                                return Collections.singletonList(Collections.<T>emptyList());
103                        return Collections.<List<T>>emptyList();
104                }
105                
106                return new Iterable<List<T>>() {
107                        @Override
108                        public Iterator<List<T>> iterator() {
109                                return new AllTuplesIterator<>(domain, minLength, maxLength);
110                        }
111                };
112        }
113        
114        public static <T> Iterable<List<T>> allTuples(final Iterable<T> domain, final int length) {
115                return allTuples(domain, length, length);
116        }
117        
118        @SafeVarargs
119        public static <T> Iterable<List<T>> allCombinations(final Iterable<T> ...iterables) {
120                if(iterables.length == 0)
121                        return Collections.singletonList(Collections.<T>emptyList());
122                
123                return new Iterable<List<T>>() {
124                        @Override
125                        public Iterator<List<T>> iterator() {
126                                try {
127                                        return new AllCombinationsIterator<>(iterables);
128                                }
129                                catch(NoSuchElementException ex) {
130                                        // FIXME: Special case if one of the iterables is empty, then the whole set
131                                        // of combinations is empty. Maybe handle this w/o exception?
132                                        return Collections.<List<T>>emptySet().iterator();
133                                }
134                        }
135                };
136        }
137
138}