Source for org.jfree.data.KeyToGroupMap

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
   6:  *
   7:  * Project Info:  http://www.jfree.org/jfreechart/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  *
  27:  * ------------------
  28:  * KeyToGroupMap.java
  29:  * ------------------
  30:  * (C) Copyright 2004, 2005, by Object Refinery Limited and Contributors.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   -;
  34:  *
  35:  * $Id: KeyToGroupMap.java,v 1.7.2.2 2005/10/25 21:29:13 mungady Exp $
  36:  *
  37:  * Changes
  38:  * -------
  39:  * 29-Apr-2004 : Version 1 (DG);
  40:  * 07-Jul-2004 : Added a group list to ensure group index is consistent, fixed 
  41:  *               cloning problem (DG);
  42:  * 18-Aug-2005 : Added casts in clone() method to suppress 1.5 compiler 
  43:  *               warnings - see patch 1260587 (DG);
  44:  * 
  45:  */
  46: 
  47: package org.jfree.data;
  48: 
  49: import java.io.Serializable;
  50: import java.lang.reflect.Method;
  51: import java.lang.reflect.Modifier;
  52: import java.util.ArrayList;
  53: import java.util.Collection;
  54: import java.util.HashMap;
  55: import java.util.Iterator;
  56: import java.util.List;
  57: import java.util.Map;
  58: 
  59: import org.jfree.util.ObjectUtilities;
  60: import org.jfree.util.PublicCloneable;
  61: 
  62: /**
  63:  * A class that maps keys (instances of <code>Comparable</code>) to groups.
  64:  */
  65: public class KeyToGroupMap implements Cloneable, PublicCloneable, Serializable {
  66:     
  67:     /** For serialization. */
  68:     private static final long serialVersionUID = -2228169345475318082L;
  69:     
  70:     /** The default group. */
  71:     private Comparable defaultGroup;
  72:     
  73:     /** The groups. */
  74:     private List groups;
  75:     
  76:     /** A mapping between keys and groups. */
  77:     private Map keyToGroupMap;
  78:     
  79:     /**
  80:      * Creates a new map with a default group named 'Default Group'.
  81:      */
  82:     public KeyToGroupMap() {
  83:         this("Default Group");
  84:     }
  85:     
  86:     /**
  87:      * Creates a new map with the specified default group.
  88:      * 
  89:      * @param defaultGroup  the default group (<code>null</code> not permitted).
  90:      */
  91:     public KeyToGroupMap(Comparable defaultGroup) {
  92:         if (defaultGroup == null) {
  93:             throw new IllegalArgumentException("Null 'defaultGroup' argument.");
  94:         }
  95:         this.defaultGroup = defaultGroup;
  96:         this.groups = new ArrayList();
  97:         this.keyToGroupMap = new HashMap();
  98:     }
  99:     
 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: }