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 java.util.Collection;
024    import java.util.HashSet;
025    import java.util.Iterator;
026    import java.util.Set;
027    
028    /** A special Collection class that is like a Map but which allows duplicate keys.
029     * Only duplicate key/value pairs aren't allowed.
030     * The key therefor is named first, the value second.
031     * Uniqueness is gained with Key/Value pairs.
032     * @author <a href="mailto:chris@riedquat.de">Christian Hujer</a>
033     */
034    @SuppressWarnings({"BooleanMethodNameMustStartWithQuestion"})
035    public class Table<T1,T2> {
036    
037        /** The pairs of the table. */
038        private Set<Pair<T1,T2>> pairs = new HashSet<Pair<T1,T2>>();
039    
040        /** Create a table. */
041        public Table() {
042        }
043    
044        /** Completely Clear the table. */
045        public void clear() {
046            pairs.clear();
047        }
048    
049        /** Get all firsts that match the second.
050         * @param second second to match
051         * @return all firsts that match second
052         */
053        public Collection<T1> getFirstsBySecond(final T2 second) {
054            final Set<T1> firsts = new HashSet<T1>();
055            for (final Pair<T1, T2> pair : pairs) {
056                final T2 pairSecond = pair.getSecond();
057                if (pairSecond.equals(second)) {
058                    firsts.add(pair.getFirst());
059                }
060            }
061            return firsts;
062        }
063    
064        /** Get all pairs that match the first.
065         * @param first first to match
066         * @return all pairs that match first
067         */
068        public Collection<Pair<T1,T2>> getPairsByFirst(final T1 first) {
069            final Set<Pair<T1,T2>> pairsByFirst = new HashSet<Pair<T1,T2>>();
070            for (Pair<T1, T2> pair : pairs) {
071                final T1 pairFirst = pair.getFirst();
072                if (pairFirst.equals(first)) {
073                    pairsByFirst.add(pair);
074                }
075            }
076            return pairsByFirst;
077        }
078    
079        /** Get all pairs that match the second.
080         * @param second second to match
081         * @return all pairs that match second
082         */
083        public Collection<Pair<T1,T2>> getPairsBySecond(final T2 second) {
084            final Set<Pair<T1,T2>> pairsBySecond = new HashSet<Pair<T1,T2>>();
085            for (Pair<T1, T2> pair : pairs) {
086                final T2 pairSecond = pair.getSecond();
087                if (pairSecond.equals(second)) {
088                    pairsBySecond.add(pair);
089                }
090            }
091            return pairsBySecond;
092        }
093    
094        /** Get all seconds that match the first.
095         * @param first first to match
096         * @return all seconds that match first
097         */
098        public Collection<T2> getSecondsByFirst(final T1 first) {
099            final Set<T2> seconds = new HashSet<T2>();
100            for (final Pair<T1, T2> pair : pairs) {
101                final T1 pairFirst = pair.getFirst();
102                if (pairFirst.equals(first)) {
103                    seconds.add(pair.getSecond());
104                }
105            }
106            return seconds;
107        }
108    
109        /** Put a pair into the table.
110         * @param pair pair to put into the table
111         * @return <code>true</code> if the table did not already contain that <var>pair</var>, otherwise <code>false</code>
112         */
113        public boolean putPair(final Pair<T1,T2> pair) {
114            return pairs.add(pair);
115        }
116    
117        /** Put a pair into the table.
118         * @param t1 first of pair
119         * @param t2 second of pair
120         * @return <code>true</code> if the table did not already contain that <var>pair</var>, otherwise <code>false</code>
121         */
122        public boolean putPair(final T1 t1, final T2 t2) {
123            return pairs.add(new Pair<T1,T2>(t1, t2));
124        }
125    
126        /** Remove all pairs that match a first.
127         * @param first first to match
128         * @return whether a matching pair was removed
129         * @retval <code>true</code> if at least one pair was removed
130         * @retval <code>false</code> if no matching pairs were found
131         */
132        public boolean removeAllFirst(final T1 first) {
133            boolean ret = false;
134            //noinspection ForLoopWithMissingComponent
135            for (final Iterator<Pair<T1,T2>> it = pairs.iterator(); it.hasNext();) {
136                final Pair<T1,T2> pair = it.next();
137                final T1 pairFirst = pair.getFirst();
138                if (pairFirst.equals(first)) {
139                    it.remove();
140                    ret = true;
141                }
142            }
143            return ret;
144        }
145    
146        /** Remove all pairs that match a second.
147         * @param second second to match
148         * @return whether a matching pair was removed
149         * @retval <code>true</code> if at least one pair was removed
150         * @retval <code>false</code> if no matching pairs were found
151         */
152        public boolean removeAllSecond(final T2 second) {
153            boolean ret = false;
154            //noinspection ForLoopWithMissingComponent
155            for (final Iterator<Pair<T1,T2>> it = pairs.iterator(); it.hasNext();) {
156                final Pair<T1,T2> pair = it.next();
157                final T2 pairSecond = pair.getSecond();
158                if (pairSecond.equals(second)) {
159                    it.remove();
160                    ret = true;
161                }
162            }
163            return ret;
164        }
165    
166        /** Remove a pair from the table.
167         * @param pair pair to remove
168         * @return <code>true</code> if the table contained the <var>pair</var>, thus the <var>pair</var> was successfully removed, otherwise <code>false</code>
169         */
170        public boolean removePair(final Pair<T1,T2> pair) {
171            return pairs.remove(pair);
172        }
173    
174        /** Remove a pair into the table.
175         * @param t1 first of pair
176         * @param t2 second of pair
177         * @return <code>true</code> if the table did not already contain that <var>pair</var>, otherwise <code>false</code>
178         */
179        public boolean removePair(final T1 t1, final T2 t2) {
180            return pairs.remove(new Pair<T1,T2>(t1, t2));
181        }
182    
183        /** Get the size of the table.
184         * @return size of the table
185         */
186        public int size() {
187            return pairs.size();
188        }
189    
190    } // class Table