Source for org.jfree.data.KeyToGroupMap

   1: /* ===========================================================
   2:  * JFreeChart : a free chart library for the Java(tm) platform
   3:  * ===========================================================
   4:  *
   5:  * (C) Copyright 2000-2007, 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-2007, by Object Refinery Limited and Contributors.
  31:  *
  32:  * Original Author:  David Gilbert (for Object Refinery Limited);
  33:  * Contributor(s):   -;
  34:  *
  35:  * Changes
  36:  * -------
  37:  * 29-Apr-2004 : Version 1 (DG);
  38:  * 07-Jul-2004 : Added a group list to ensure group index is consistent, fixed 
  39:  *               cloning problem (DG);
  40:  * 18-Aug-2005 : Added casts in clone() method to suppress 1.5 compiler 
  41:  *               warnings - see patch 1260587 (DG);
  42:  * 
  43:  */
  44: 
  45: package org.jfree.data;
  46: 
  47: import java.io.Serializable;
  48: import java.lang.reflect.Method;
  49: import java.lang.reflect.Modifier;
  50: import java.util.ArrayList;
  51: import java.util.Collection;
  52: import java.util.HashMap;
  53: import java.util.Iterator;
  54: import java.util.List;
  55: import java.util.Map;
  56: 
  57: import org.jfree.util.ObjectUtilities;
  58: import org.jfree.util.PublicCloneable;
  59: 
  60: /**
  61:  * A class that maps keys (instances of <code>Comparable</code>) to groups.
  62:  */
  63: public class KeyToGroupMap implements Cloneable, PublicCloneable, Serializable {
  64:     
  65:     /** For serialization. */
  66:     private static final long serialVersionUID = -2228169345475318082L;
  67:     
  68:     /** The default group. */
  69:     private Comparable defaultGroup;
  70:     
  71:     /** The groups. */
  72:     private List groups;
  73:     
  74:     /** A mapping between keys and groups. */
  75:     private Map keyToGroupMap;
  76:     
  77:     /**
  78:      * Creates a new map with a default group named 'Default Group'.
  79:      */
  80:     public KeyToGroupMap() {
  81:         this("Default Group");
  82:     }
  83:     
  84:     /**
  85:      * Creates a new map with the specified default group.
  86:      * 
  87:      * @param defaultGroup  the default group (<code>null</code> not permitted).
  88:      */
  89:     public KeyToGroupMap(Comparable defaultGroup) {
  90:         if (defaultGroup == null) {
  91:             throw new IllegalArgumentException("Null 'defaultGroup' argument.");
  92:         }
  93:         this.defaultGroup = defaultGroup;
  94:         this.groups = new ArrayList();
  95:         this.keyToGroupMap = new HashMap();
  96:     }
  97:     
  98:     /**
  99:      * Returns the number of groups in the map.
 100:      * 
 101:      * @return The number of groups in the map.
 102:      */
 103:     public int getGroupCount() {
 104:         return this.groups.size() + 1;
 105:     }
 106:     
 107:     /**
 108:      * Returns a list of the groups (always including the default group) in the 
 109:      * map.  The returned list is independent of the map, so altering the list 
 110:      * will have no effect.
 111:      * 
 112:      * @return The groups (never <code>null</code>).
 113:      */
 114:     public List getGroups() {
 115:         List result = new ArrayList();
 116:         result.add(this.defaultGroup);
 117:         Iterator iterator = this.groups.iterator();
 118:         while (iterator.hasNext()) {
 119:             Comparable group = (Comparable) iterator.next();
 120:             if (!result.contains(group)) {
 121:                 result.add(group);   
 122:             }
 123:         } 
 124:         return result;
 125:     }
 126:     
 127:     /**
 128:      * Returns the index for the group.
 129:      * 
 130:      * @param group  the group.
 131:      * 
 132:      * @return The group index (or -1 if the group is not represented within 
 133:      *         the map).
 134:      */
 135:     public int getGroupIndex(Comparable group) {
 136:         int result = this.groups.indexOf(group);
 137:         if (result < 0) {
 138:             if (this.defaultGroup.equals(group)) {
 139:                 result = 0;
 140:             }
 141:         }
 142:         else {
 143:             result = result + 1;   
 144:         }
 145:         return result;   
 146:     }
 147:     
 148:     /**
 149:      * Returns the group that a key is mapped to.
 150:      * 
 151:      * @param key  the key (<code>null</code> not permitted).
 152:      * 
 153:      * @return The group (never <code>null</code>, returns the default group if
 154:      *         there is no mapping for the specified key).
 155:      */
 156:     public Comparable getGroup(Comparable key) {
 157:         if (key == null) {
 158:             throw new IllegalArgumentException("Null 'key' argument.");   
 159:         }
 160:         Comparable result = this.defaultGroup;
 161:         Comparable group = (Comparable) this.keyToGroupMap.get(key);
 162:         if (group != null) {
 163:             result = group;   
 164:         }
 165:         return result;
 166:     }
 167:     
 168:     /**
 169:      * Maps a key to a group.
 170:      * 
 171:      * @param key  the key (<code>null</code> not permitted).
 172:      * @param group  the group (<code>null</code> permitted, clears any 
 173:      *               existing mapping).
 174:      */
 175:     public void mapKeyToGroup(Comparable key, Comparable group) {
 176:         if (key == null) {
 177:             throw new IllegalArgumentException("Null 'key' argument.");   
 178:         }
 179:         Comparable currentGroup = getGroup(key);
 180:         if (!currentGroup.equals(this.defaultGroup)) {
 181:             if (!currentGroup.equals(group)) {
 182:                 int count = getKeyCount(currentGroup);
 183:                 if (count == 1) {
 184:                     this.groups.remove(currentGroup);   
 185:                 }
 186:             }
 187:         }
 188:         if (group == null) {
 189:             this.keyToGroupMap.remove(key); 
 190:         }
 191:         else {
 192:             if (!this.groups.contains(group)) {
 193:                 if (!this.defaultGroup.equals(group)) {
 194:                     this.groups.add(group);
 195:                 }
 196:             }
 197:             this.keyToGroupMap.put(key, group);
 198:         }
 199:     }
 200:     
 201:     /**
 202:      * Returns the number of keys mapped to the specified group.  This method 
 203:      * won't always return an accurate result for the default group, since 
 204:      * explicit mappings are not required for this group.
 205:      * 
 206:      * @param group  the group (<code>null</code> not permitted).
 207:      * 
 208:      * @return The key count.
 209:      */
 210:     public int getKeyCount(Comparable group) {
 211:         if (group == null) {
 212:             throw new IllegalArgumentException("Null 'group' argument.");   
 213:         }
 214:         int result = 0;
 215:         Iterator iterator = this.keyToGroupMap.values().iterator();
 216:         while (iterator.hasNext()) {
 217:             Comparable g = (Comparable) iterator.next();
 218:             if (group.equals(g)) {
 219:                 result++;
 220:             }
 221:         }
 222:         return result;
 223:     }
 224:     
 225:     /**
 226:      * Tests the map for equality against an arbitrary object.
 227:      * 
 228:      * @param obj  the object to test against (<code>null</code> permitted).
 229:      * 
 230:      * @return A boolean.
 231:      */
 232:     public boolean equals(Object obj) {
 233:         if (obj == this) {
 234:             return true;      
 235:         }
 236:         if (!(obj instanceof KeyToGroupMap)) {
 237:             return false;
 238:         }
 239:         KeyToGroupMap that = (KeyToGroupMap) obj;
 240:         if (!ObjectUtilities.equal(this.defaultGroup, that.defaultGroup)) {
 241:             return false;
 242:         }
 243:         if (!this.keyToGroupMap.equals(that.keyToGroupMap)) {
 244:             return false;
 245:         }
 246:         return true;
 247:     }
 248:     
 249:     /**
 250:      * Returns a clone of the map.
 251:      * 
 252:      * @return A clone.
 253:      * 
 254:      * @throws CloneNotSupportedException  if there is a problem cloning the
 255:      *                                     map.
 256:      */
 257:     public Object clone() throws CloneNotSupportedException {
 258:         KeyToGroupMap result = (KeyToGroupMap) super.clone();
 259:         result.defaultGroup 
 260:             = (Comparable) KeyToGroupMap.clone(this.defaultGroup);
 261:         result.groups = (List) KeyToGroupMap.clone(this.groups);
 262:         result.keyToGroupMap = (Map) KeyToGroupMap.clone(this.keyToGroupMap);
 263:         return result;
 264:     }
 265:     
 266:     /**
 267:      * Attempts to clone the specified object using reflection.
 268:      * 
 269:      * @param object  the object (<code>null</code> permitted).
 270:      * 
 271:      * @return The cloned object, or the original object if cloning failed.
 272:      */
 273:     private static Object clone(Object object) {
 274:         if (object == null) {
 275:             return null;   
 276:         }
 277:         Class c = object.getClass();
 278:         Object result = null;
 279:         try {
 280:             Method m = c.getMethod("clone", (Class[]) null);
 281:             if (Modifier.isPublic(m.getModifiers())) {
 282:                 try {
 283:                     result = m.invoke(object, (Object[]) null);
 284:                 }
 285:                 catch (Exception e) {
 286:                     e.printStackTrace();  
 287:                 }
 288:             }
 289:         }
 290:         catch (NoSuchMethodException e) {
 291:             result = object;
 292:         }
 293:         return result;
 294:     }
 295:     
 296:     /**
 297:      * Returns a clone of the list.
 298:      * 
 299:      * @param list  the list.
 300:      * 
 301:      * @return A clone of the list.
 302:      * 
 303:      * @throws CloneNotSupportedException if the list could not be cloned.
 304:      */
 305:     private static Collection clone(Collection list) 
 306:         throws CloneNotSupportedException {
 307:         Collection result = null;
 308:         if (list != null) {
 309:             try {
 310:                 List clone = (List) list.getClass().newInstance();
 311:                 Iterator iterator = list.iterator();
 312:                 while (iterator.hasNext()) {
 313:                     clone.add(KeyToGroupMap.clone(iterator.next()));
 314:                 }
 315:                 result = clone;
 316:             }
 317:             catch (Exception e) {
 318:                 throw new CloneNotSupportedException("Exception.");
 319:             }
 320:         }
 321:         return result;
 322:     }
 323: 
 324: }