    001    /* ===========================================================
    002     * JFreeChart : a free chart library for the Java(tm) platform
    003     * ===========================================================
    004     *
    005     * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
    006     *
    007     * Project Info:  http://www.jfree.org/jfreechart/index.html
    008     *
    009     * This library is free software; you can redistribute it and/or modify it
    010     * under the terms of the GNU Lesser General Public License as published by
    011     * the Free Software Foundation; either version 2.1 of the License, or
    012     * (at your option) any later version.
    013     *
    014     * This library is distributed in the hope that it will be useful, but
    015     * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
    016     * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
    017     * License for more details.
    018     *
    019     * You should have received a copy of the GNU Lesser General Public
    020     * License along with this library; if not, write to the Free Software
    021     * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
    022     * USA.
    023     *
    024     * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
    025     * in the United States and other countries.]
    026     *
    027     * ------------------
    028     * KeyToGroupMap.java
    029     * ------------------
    030     * (C) Copyright 2004, 2005, by Object Refinery Limited and Contributors.
    031     *
    032     * Original Author:  David Gilbert (for Object Refinery Limited);
    033     * Contributor(s):   -;
    034     *
    035     * $Id: KeyToGroupMap.java,v 1.7.2.2 2005/10/25 21:29:13 mungady Exp $
    036     *
    037     * Changes
    038     * -------
    039     * 29-Apr-2004 : Version 1 (DG);
    040     * 07-Jul-2004 : Added a group list to ensure group index is consistent, fixed
    041     *               cloning problem (DG);
    042     * 18-Aug-2005 : Added casts in clone() method to suppress 1.5 compiler
    043     *               warnings - see patch 1260587 (DG);
    044     *
    045     */
    046
    047    package org.jfree.data;
    048
    049    import java.io.Serializable;
    050    import java.lang.reflect.Method;
    051    import java.lang.reflect.Modifier;
    052    import java.util.ArrayList;
    053    import java.util.Collection;
    054    import java.util.HashMap;
    055    import java.util.Iterator;
    056    import java.util.List;
    057    import java.util.Map;
    058
    059    import org.jfree.util.ObjectUtilities;
    060    import org.jfree.util.PublicCloneable;
    061
    062    /**
    063     * A class that maps keys (instances of <code>Comparable</code>) to groups.
    064     */
    065    public class KeyToGroupMap implements Cloneable, PublicCloneable, Serializable {
    066
    067        /** For serialization. */
    068        private static final long serialVersionUID = -2228169345475318082L;
    069
    070        /** The default group. */
    071        private Comparable defaultGroup;
    072
    073        /** The groups. */
    074        private List groups;
    075
    076        /** A mapping between keys and groups. */
    077        private Map keyToGroupMap;
    078
    079        /**
    080         * Creates a new map with a default group named 'Default Group'.
    081         */
    082        public KeyToGroupMap() {
    083            this("Default Group");
    084        }
    085
    086        /**
    087         * Creates a new map with the specified default group.
    088         *
    089         * @param defaultGroup  the default group (<code>null</code> not permitted).
    090         */
    091        public KeyToGroupMap(Comparable defaultGroup) {
    092            if (defaultGroup == null) {
    093                throw new IllegalArgumentException("Null 'defaultGroup' argument.");
    094            }
    095            this.defaultGroup = defaultGroup;
    096            this.groups = new ArrayList();
    097            this.keyToGroupMap = new HashMap();
    098        }
    099
    100        /**
    101         * Returns the number of groups in the map.
    102         *
    103         * @return The number of groups in the map.
    104         */
    105        public int getGroupCount() {
    106            return this.groups.size() + 1;
    107        }
    108
    109        /**
    110         * Returns a list of the groups (always including the default group) in the
    111         * map.  The returned list is independent of the map, so altering the list
    112         * will have no effect.
    113         *
    114         * @return The groups (never <code>null</code>).
    115         */
    116        public List getGroups() {
    117            List result = new ArrayList();
    118            result.add(this.defaultGroup);
    119            Iterator iterator = this.groups.iterator();
    120            while (iterator.hasNext()) {
    121                Comparable group = (Comparable) iterator.next();
    122                if (!result.contains(group)) {
    123                    result.add(group);
    124                }
    125            }
    126            return result;
    127        }
    128
    129        /**
    130         * Returns the index for the group.
    131         *
    132         * @param group  the group.
    133         *
    134         * @return The group index (or -1 if the group is not represented within
    135         *         the map).
    136         */
    137        public int getGroupIndex(Comparable group) {
    138            int result = this.groups.indexOf(group);
    139            if (result < 0) {
    140                if (this.defaultGroup.equals(group)) {
    141                    result = 0;
    142                }
    143            }
    144            else {
    145                result = result + 1;
    146            }
    147            return result;
    148        }
    149
    150        /**
    151         * Returns the group that a key is mapped to.
    152         *
    153         * @param key  the key (<code>null</code> not permitted).
    154         *
    155         * @return The group (never <code>null</code>, returns the default group if
    156         *         there is no mapping for the specified key).
    157         */
    158        public Comparable getGroup(Comparable key) {
    159            if (key == null) {
    160                throw new IllegalArgumentException("Null 'key' argument.");
    161            }
    162            Comparable result = this.defaultGroup;
    163            Comparable group = (Comparable) this.keyToGroupMap.get(key);
    164            if (group != null) {
    165                result = group;
    166            }
    167            return result;
    168        }
    169
    170        /**
    171         * Maps a key to a group.
    172         *
    173         * @param key  the key (<code>null</code> not permitted).
    174         * @param group  the group (<code>null</code> permitted, clears any
    175         *               existing mapping).
    176         */
    177        public void mapKeyToGroup(Comparable key, Comparable group) {
    178            if (key == null) {
    179                throw new IllegalArgumentException("Null 'key' argument.");
    180            }
    181            Comparable currentGroup = getGroup(key);
    182            if (!currentGroup.equals(this.defaultGroup)) {
    183                if (!currentGroup.equals(group)) {
    184                    int count = getKeyCount(currentGroup);
    185                    if (count == 1) {
    186                        this.groups.remove(currentGroup);
    187                    }
    188                }
    189            }
    190            if (group == null) {
    191                this.keyToGroupMap.remove(key);
    192            }
    193            else {
    194                if (!this.groups.contains(group)) {
    195                    if (!this.defaultGroup.equals(group)) {
    196                        this.groups.add(group);
    197                    }
    198                }
    199                this.keyToGroupMap.put(key, group);
    200            }
    201        }
    202
    203        /**
    204         * Returns the number of keys mapped to the specified group.  This method
    205         * won't always return an accurate result for the default group, since
    206         * explicit mappings are not required for this group.
    207         *
    208         * @param group  the group (<code>null</code> not permitted).
    209         *
    210         * @return The key count.
    211         */
    212        public int getKeyCount(Comparable group) {
    213            if (group == null) {
    214                throw new IllegalArgumentException("Null 'group' argument.");
    215            }
    216            int result = 0;
    217            Iterator iterator = this.keyToGroupMap.values().iterator();
    218            while (iterator.hasNext()) {
    219                Comparable g = (Comparable) iterator.next();
    220                if (group.equals(g)) {
    221                    result++;
    222                }
    223            }
    224            return result;
    225        }
    226
    227        /**
    228         * Tests the map for equality against an arbitrary object.
    229         *
    230         * @param obj  the object to test against (<code>null</code> permitted).
    231         *
    232         * @return A boolean.
    233         */
    234        public boolean equals(Object obj) {
    235            if (obj == this) {
    236                return true;
    237            }
    238            if (!(obj instanceof KeyToGroupMap)) {
    239                return false;
    240            }
    241            KeyToGroupMap that = (KeyToGroupMap) obj;
    242            if (!ObjectUtilities.equal(this.defaultGroup, that.defaultGroup)) {
    243                return false;
    244            }
    245            if (!this.keyToGroupMap.equals(that.keyToGroupMap)) {
    246                return false;
    247            }
    248            return true;
    249        }
    250
    251        /**
    252         * Returns a clone of the map.
    253         *
    254         * @return A clone.
    255         *
    256         * @throws CloneNotSupportedException  if there is a problem cloning the
    257         *                                     map.
    258         */
    259        public Object clone() throws CloneNotSupportedException {
    260            KeyToGroupMap result = (KeyToGroupMap) super.clone();
    261            result.defaultGroup
    262                = (Comparable) KeyToGroupMap.clone(this.defaultGroup);
    263            result.groups = (List) KeyToGroupMap.clone(this.groups);
    264            result.keyToGroupMap = (Map) KeyToGroupMap.clone(this.keyToGroupMap);
    265            return result;
    266        }
    267
    268        /**
    269         * Attempts to clone the specified object using reflection.
    270         *
    271         * @param object  the object (<code>null</code> permitted).
    272         *
    273         * @return The cloned object, or the original object if cloning failed.
    274         */
    275        private static Object clone(Object object) {
    276            if (object == null) {
    277                return null;
    278            }
    279            Class c = object.getClass();
    280            Object result = null;
    281            try {
    282                Method m = c.getMethod("clone", (Class[]) null);
    283                if (Modifier.isPublic(m.getModifiers())) {
    284                    try {
    285                        result = m.invoke(object, (Object[]) null);
    286                    }
    287                    catch (Exception e) {
    288                        e.printStackTrace();
    289                    }
    290                }
    291            }
    292            catch (NoSuchMethodException e) {
    293                result = object;
    294            }
    295            return result;
    296        }
    297
    298        /**
    299         * Returns a clone of the list.
    300         *
    301         * @param list  the list.
    302         *
    303         * @return A clone of the list.
    304         *
    305         * @throws CloneNotSupportedException if the list could not be cloned.
    306         */
    307        private static Collection clone(Collection list)
    308            throws CloneNotSupportedException {
    309            Collection result = null;
    310            if (list != null) {
    311                try {
    312                    List clone = (List) list.getClass().newInstance();
    313                    Iterator iterator = list.iterator();
    314                    while (iterator.hasNext()) {
    315                        clone.add(KeyToGroupMap.clone(iterator.next()));
    316                    }
    317                    result = clone;
    318                }
    319                catch (Exception e) {
    320                    throw new CloneNotSupportedException("Exception.");
    321                }
    322            }
    323            return result;
    324        }
    325
    326    }