001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-2013, 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 * [Oracle and Java are registered trademarks of Oracle and/or its affiliates. 025 * Other names may be trademarks of their respective owners.] 026 * 027 * ------------------ 028 * KeyToGroupMap.java 029 * ------------------ 030 * (C) Copyright 2004-2013, 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 * 03-Jul-2013 : Use ParamChecks (DG); 043 * 044 */ 045 046package org.jfree.data; 047 048import java.io.Serializable; 049import java.lang.reflect.Method; 050import java.lang.reflect.Modifier; 051import java.util.ArrayList; 052import java.util.Collection; 053import java.util.HashMap; 054import java.util.Iterator; 055import java.util.List; 056import java.util.Map; 057import org.jfree.chart.util.ParamChecks; 058 059import org.jfree.util.ObjectUtilities; 060import org.jfree.util.PublicCloneable; 061 062/** 063 * A class that maps keys (instances of <code>Comparable</code>) to groups. 064 */ 065public 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 ParamChecks.nullNotPermitted(defaultGroup, "defaultGroup"); 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 ParamChecks.nullNotPermitted(key, "key"); 158 Comparable result = this.defaultGroup; 159 Comparable group = (Comparable) this.keyToGroupMap.get(key); 160 if (group != null) { 161 result = group; 162 } 163 return result; 164 } 165 166 /** 167 * Maps a key to a group. 168 * 169 * @param key the key (<code>null</code> not permitted). 170 * @param group the group (<code>null</code> permitted, clears any 171 * existing mapping). 172 */ 173 public void mapKeyToGroup(Comparable key, Comparable group) { 174 ParamChecks.nullNotPermitted(key, "key"); 175 Comparable currentGroup = getGroup(key); 176 if (!currentGroup.equals(this.defaultGroup)) { 177 if (!currentGroup.equals(group)) { 178 int count = getKeyCount(currentGroup); 179 if (count == 1) { 180 this.groups.remove(currentGroup); 181 } 182 } 183 } 184 if (group == null) { 185 this.keyToGroupMap.remove(key); 186 } 187 else { 188 if (!this.groups.contains(group)) { 189 if (!this.defaultGroup.equals(group)) { 190 this.groups.add(group); 191 } 192 } 193 this.keyToGroupMap.put(key, group); 194 } 195 } 196 197 /** 198 * Returns the number of keys mapped to the specified group. This method 199 * won't always return an accurate result for the default group, since 200 * explicit mappings are not required for this group. 201 * 202 * @param group the group (<code>null</code> not permitted). 203 * 204 * @return The key count. 205 */ 206 public int getKeyCount(Comparable group) { 207 ParamChecks.nullNotPermitted(group, "group"); 208 int result = 0; 209 Iterator iterator = this.keyToGroupMap.values().iterator(); 210 while (iterator.hasNext()) { 211 Comparable g = (Comparable) iterator.next(); 212 if (group.equals(g)) { 213 result++; 214 } 215 } 216 return result; 217 } 218 219 /** 220 * Tests the map for equality against an arbitrary object. 221 * 222 * @param obj the object to test against (<code>null</code> permitted). 223 * 224 * @return A boolean. 225 */ 226 @Override 227 public boolean equals(Object obj) { 228 if (obj == this) { 229 return true; 230 } 231 if (!(obj instanceof KeyToGroupMap)) { 232 return false; 233 } 234 KeyToGroupMap that = (KeyToGroupMap) obj; 235 if (!ObjectUtilities.equal(this.defaultGroup, that.defaultGroup)) { 236 return false; 237 } 238 if (!this.keyToGroupMap.equals(that.keyToGroupMap)) { 239 return false; 240 } 241 return true; 242 } 243 244 /** 245 * Returns a clone of the map. 246 * 247 * @return A clone. 248 * 249 * @throws CloneNotSupportedException if there is a problem cloning the 250 * map. 251 */ 252 @Override 253 public Object clone() throws CloneNotSupportedException { 254 KeyToGroupMap result = (KeyToGroupMap) super.clone(); 255 result.defaultGroup 256 = (Comparable) KeyToGroupMap.clone(this.defaultGroup); 257 result.groups = (List) KeyToGroupMap.clone(this.groups); 258 result.keyToGroupMap = (Map) KeyToGroupMap.clone(this.keyToGroupMap); 259 return result; 260 } 261 262 /** 263 * Attempts to clone the specified object using reflection. 264 * 265 * @param object the object (<code>null</code> permitted). 266 * 267 * @return The cloned object, or the original object if cloning failed. 268 */ 269 private static Object clone(Object object) { 270 if (object == null) { 271 return null; 272 } 273 Class c = object.getClass(); 274 Object result = null; 275 try { 276 Method m = c.getMethod("clone", (Class[]) null); 277 if (Modifier.isPublic(m.getModifiers())) { 278 try { 279 result = m.invoke(object, (Object[]) null); 280 } 281 catch (Exception e) { 282 e.printStackTrace(); 283 } 284 } 285 } 286 catch (NoSuchMethodException e) { 287 result = object; 288 } 289 return result; 290 } 291 292 /** 293 * Returns a clone of the list. 294 * 295 * @param list the list. 296 * 297 * @return A clone of the list. 298 * 299 * @throws CloneNotSupportedException if the list could not be cloned. 300 */ 301 private static Collection clone(Collection list) 302 throws CloneNotSupportedException { 303 Collection result = null; 304 if (list != null) { 305 try { 306 List clone = (List) list.getClass().newInstance(); 307 Iterator iterator = list.iterator(); 308 while (iterator.hasNext()) { 309 clone.add(KeyToGroupMap.clone(iterator.next())); 310 } 311 result = clone; 312 } 313 catch (Exception e) { 314 throw new CloneNotSupportedException("Exception."); 315 } 316 } 317 return result; 318 } 319 320}