001 /* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2008, 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-2008, by Object Refinery Limited and Contributors. 031 * 032 * Original Author: David Gilbert (for Object Refinery Limited); 033 * Contributor(s): -; 034 * 035 * Changes 036 * ------- 037 * 29-Apr-2004 : Version 1 (DG); 038 * 07-Jul-2004 : Added a group list to ensure group index is consistent, fixed 039 * cloning problem (DG); 040 * 18-Aug-2005 : Added casts in clone() method to suppress 1.5 compiler 041 * warnings - see patch 1260587 (DG); 042 * 043 */ 044 045 package org.jfree.data; 046 047 import java.io.Serializable; 048 import java.lang.reflect.Method; 049 import java.lang.reflect.Modifier; 050 import java.util.ArrayList; 051 import java.util.Collection; 052 import java.util.HashMap; 053 import java.util.Iterator; 054 import java.util.List; 055 import java.util.Map; 056 057 import org.jfree.util.ObjectUtilities; 058 import org.jfree.util.PublicCloneable; 059 060 /** 061 * A class that maps keys (instances of <code>Comparable</code>) to groups. 062 */ 063 public class KeyToGroupMap implements Cloneable, PublicCloneable, Serializable { 064 065 /** For serialization. */ 066 private static final long serialVersionUID = -2228169345475318082L; 067 068 /** The default group. */ 069 private Comparable defaultGroup; 070 071 /** The groups. */ 072 private List groups; 073 074 /** A mapping between keys and groups. */ 075 private Map keyToGroupMap; 076 077 /** 078 * Creates a new map with a default group named 'Default Group'. 079 */ 080 public KeyToGroupMap() { 081 this("Default Group"); 082 } 083 084 /** 085 * Creates a new map with the specified default group. 086 * 087 * @param defaultGroup the default group (<code>null</code> not permitted). 088 */ 089 public KeyToGroupMap(Comparable defaultGroup) { 090 if (defaultGroup == null) { 091 throw new IllegalArgumentException("Null 'defaultGroup' argument."); 092 } 093 this.defaultGroup = defaultGroup; 094 this.groups = new ArrayList(); 095 this.keyToGroupMap = new HashMap(); 096 } 097 098 /** 099 * 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 }