001    /* JAPI - (Yet another (hopefully) useful) Java API
002     *
003     * Copyright (C) 2004-2006 Christian Hujer
004     *
005     * This program is free software; you can redistribute it and/or
006     * modify it under the terms of the GNU General Public License as
007     * published by the Free Software Foundation; either version 2 of the
008     * License, or (at your option) any later version.
009     *
010     * This program is distributed in the hope that it will be useful, but
011     * WITHOUT ANY WARRANTY; without even the implied warranty of
012     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013     * General Public License for more details.
014     *
015     * You should have received a copy of the GNU General Public License
016     * along with this program; if not, write to the Free Software
017     * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
018     * 02111-1307, USA.
019     */
020    
021    package net.sf.japi.util;
022    
023    import static java.lang.Class.forName;
024    import java.util.ArrayList;
025    import java.util.Collection;
026    import java.util.Collections;
027    import java.util.List;
028    import java.util.RandomAccess;
029    import java.util.Iterator;
030    import java.util.Comparator;
031    import net.sf.japi.util.filter.Filter;
032    
033    /** This class provides some additional utility methods you might miss in {@link Collections}.
034     * It is named Collections2 so you have no problems using both, this class and <code>java.util.Collections</code> (no name conflict).
035     * @author <a href="mailto:chris@riedquat.de">Christian Hujer</a>
036     * @see Collections
037     */
038    public class Collections2 {
039    
040        /** Class of java.util.Arrays.ArrayList */
041        private static final Class<? extends List<?>> AL;
042        static {
043            Class<? extends List<?>> c = null;
044            try {
045                c = (Class<? extends List<?>>) forName("java.util.Arrays.ArrayList");
046            } catch (ClassNotFoundException e) { /* ignore, null is okay then. */ }
047            AL = c;
048        }
049    
050        /** Returns a collection only containing those elements accepted by the given filter.
051         * The original collection remains unmodified.
052         * This method tries its best to create a new, empty variant of the Collection passed in <var>c</var>:
053         * <ol>
054         *  <li>it's tried to create a new instance from the class of <var>c</var> using Reflection</li>
055         *  <li>it's tried to {@link Object#clone()} <var>c</var> using Reflection and {@link Collection#clear()} the clone</li>
056         *  <li>it's tried if c is a {@link RandomAccess} {@link List}, in which case an {@link ArrayList} is returned as an alternative</li>
057         * </ol>
058         * @param c Collection to filter
059         * @param filter Filter to use for <var>c</var>
060         * @return collection containing only those elements accepted by the filter or <code>null</code> if the Collection could not be created.
061         */
062        public static final <T, C extends Collection<T>> C filter(final C c, final Filter<? super T> filter) {
063            C filtered = null;
064            try {
065                filtered = (C) c.getClass().newInstance();
066            } catch (final Exception e) { /* ignore, check is done on null. */ }
067            if (filtered == null && c instanceof Cloneable) {
068                try {
069                    filtered = (C) c.getClass().getMethod("clone").invoke(c);
070                    c.clear();
071                } catch (final Exception e) {
072                    filtered = null;
073                }
074            }
075            if (filtered == null && (AL != null && AL.isInstance(c) || c instanceof List && c instanceof RandomAccess)) {
076                filtered = (C) new ArrayList<T>();
077            }
078            if (filtered != null) { // only fill if filtered collection could be created.
079                for (final T o : c) {
080                    if (filter.accept(o)) {
081                        filtered.add(o);
082                    }
083                }
084            }
085            return filtered;
086        }
087    
088        /** Removes all elements from a collection not accepted by the given filter.
089         * The original collection is modified.
090         * It is required that the collection is modifiable.
091         * @param collection Collection to filter
092         * @param filter Filter to use for <var>c</var>
093         * @return number of elements removed
094         */
095        public static final <T, C extends Collection<T>> int removeFilter(final C collection, final Filter<? super T> filter) {
096            int removed = 0;
097            for (final T o : collection) {
098                if (!filter.accept(o)) {
099                    collection.remove(o);
100                    removed++;
101                }
102            }
103            return removed;
104        }
105    
106        /** Checks whether a list is sorted.
107         * @param list List to check
108         * @return <code>true</code> if <var>list</var> is sorted, otherwise <code>false</code>
109         * @see Comparable
110         * @see Collections#sort(List)
111         */
112        public static final <T extends Comparable<? super T>> boolean isSorted(final List<T> list) {
113            if (list.size() < 2) {
114                // empty lists or lists with only 1 element are always sorted, no need to check
115                return true;
116            }
117            if (list instanceof RandomAccess) {
118                final int n = list.size();
119                for (int i = 0; i < n; i++) {
120                    if (list.get(i).compareTo(list.get(i + 1)) < 0) {
121                        return false;
122                    }
123                }
124                return true;
125            } else {
126                final Iterator<T> it = list.iterator();
127                assert list.size() >= 1; // list.size() is >= 2, but >= 1 is sufficient
128                T previous = it.next(); // save because list.size() >= 1;
129                while (it.hasNext()) {
130                    final T current = it.next();
131                    if (previous.compareTo(current) < 0) {
132                        return false;
133                    }
134                    previous = current;
135                }
136                return true;
137            }
138        }
139    
140        /** Checks whether a list is sorted.
141         * @param list List to check
142         * @param c Comparator to use for comparing list elements
143         * @return <code>true</code> if <var>list</var> is sorted, otherwise <code>false</code>
144         * @see Comparator
145         * @see Collections#sort(List,Comparator)
146         */
147        public static final <T> boolean isSorted(final List<T> list, final Comparator<? super T> c) {
148            if (c == null) {
149                //noinspection unchecked,RawUseOfParameterizedType
150                return isSorted((List) list);
151            }
152            if (list.size() < 2) {
153                // empty lists or lists with only 1 element are always sorted, no need to check
154                return true;
155            }
156            if (list instanceof RandomAccess) {
157                final int n = list.size();
158                for (int i = 0; i < n; i++) {
159                    if (c.compare(list.get(i), list.get(i + 1)) < 0) {
160                        return false;
161                    }
162                }
163                return true;
164            } else {
165                final Iterator<T> it = list.iterator();
166                assert list.size() >= 1;
167                T previous = it.next();
168                while (it.hasNext()) {
169                    final T current = it.next();
170                    if (c.compare(previous, current) < 0) {
171                        return false;
172                    }
173                    previous = current;
174                }
175                return true;
176            }
177        }
178    
179        /** Private constructor - no instances needed. */
180        private Collections2() {}
181    
182    } // class Collections2