001/* =========================================================== 002 * JFreeChart : a free chart library for the Java(tm) platform 003 * =========================================================== 004 * 005 * (C) Copyright 2000-present, by David Gilbert 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-present, by David Gilbert and Contributors. 031 * 032 * Original Author: David Gilbert; 033 * Contributor(s): -; 034 * 035 */ 036 037package org.jfree.data; 038 039import java.io.Serializable; 040import java.lang.reflect.Method; 041import java.lang.reflect.Modifier; 042import java.util.ArrayList; 043import java.util.Collection; 044import java.util.HashMap; 045import java.util.Iterator; 046import java.util.List; 047import java.util.Map; 048import java.util.Objects; 049import org.jfree.chart.util.Args; 050import org.jfree.chart.util.PublicCloneable; 051 052/** 053 * A class that maps keys (instances of {@code Comparable}) to groups. 054 */ 055public class KeyToGroupMap implements Cloneable, PublicCloneable, Serializable { 056 057 /** For serialization. */ 058 private static final long serialVersionUID = -2228169345475318082L; 059 060 /** The default group. */ 061 private Comparable defaultGroup; 062 063 /** The groups. */ 064 private List groups; 065 066 /** A mapping between keys and groups. */ 067 private Map keyToGroupMap; 068 069 /** 070 * Creates a new map with a default group named 'Default Group'. 071 */ 072 public KeyToGroupMap() { 073 this("Default Group"); 074 } 075 076 /** 077 * Creates a new map with the specified default group. 078 * 079 * @param defaultGroup the default group ({@code null} not permitted). 080 */ 081 public KeyToGroupMap(Comparable defaultGroup) { 082 Args.nullNotPermitted(defaultGroup, "defaultGroup"); 083 this.defaultGroup = defaultGroup; 084 this.groups = new ArrayList(); 085 this.keyToGroupMap = new HashMap(); 086 } 087 088 /** 089 * Returns the number of groups in the map. 090 * 091 * @return The number of groups in the map. 092 */ 093 public int getGroupCount() { 094 return this.groups.size() + 1; 095 } 096 097 /** 098 * Returns a list of the groups (always including the default group) in the 099 * map. The returned list is independent of the map, so altering the list 100 * will have no effect. 101 * 102 * @return The groups (never {@code null}). 103 */ 104 public List getGroups() { 105 List result = new ArrayList(); 106 result.add(this.defaultGroup); 107 Iterator iterator = this.groups.iterator(); 108 while (iterator.hasNext()) { 109 Comparable group = (Comparable) iterator.next(); 110 if (!result.contains(group)) { 111 result.add(group); 112 } 113 } 114 return result; 115 } 116 117 /** 118 * Returns the index for the group. 119 * 120 * @param group the group. 121 * 122 * @return The group index (or -1 if the group is not represented within 123 * the map). 124 */ 125 public int getGroupIndex(Comparable group) { 126 int result = this.groups.indexOf(group); 127 if (result < 0) { 128 if (this.defaultGroup.equals(group)) { 129 result = 0; 130 } 131 } 132 else { 133 result = result + 1; 134 } 135 return result; 136 } 137 138 /** 139 * Returns the group that a key is mapped to. 140 * 141 * @param key the key ({@code null} not permitted). 142 * 143 * @return The group (never {@code null}, returns the default group if 144 * there is no mapping for the specified key). 145 */ 146 public Comparable getGroup(Comparable key) { 147 Args.nullNotPermitted(key, "key"); 148 Comparable result = this.defaultGroup; 149 Comparable group = (Comparable) this.keyToGroupMap.get(key); 150 if (group != null) { 151 result = group; 152 } 153 return result; 154 } 155 156 /** 157 * Maps a key to a group. 158 * 159 * @param key the key ({@code null} not permitted). 160 * @param group the group ({@code null} permitted, clears any 161 * existing mapping). 162 */ 163 public void mapKeyToGroup(Comparable key, Comparable group) { 164 Args.nullNotPermitted(key, "key"); 165 Comparable currentGroup = getGroup(key); 166 if (!currentGroup.equals(this.defaultGroup)) { 167 if (!currentGroup.equals(group)) { 168 int count = getKeyCount(currentGroup); 169 if (count == 1) { 170 this.groups.remove(currentGroup); 171 } 172 } 173 } 174 if (group == null) { 175 this.keyToGroupMap.remove(key); 176 } 177 else { 178 if (!this.groups.contains(group)) { 179 if (!this.defaultGroup.equals(group)) { 180 this.groups.add(group); 181 } 182 } 183 this.keyToGroupMap.put(key, group); 184 } 185 } 186 187 /** 188 * Returns the number of keys mapped to the specified group. This method 189 * won't always return an accurate result for the default group, since 190 * explicit mappings are not required for this group. 191 * 192 * @param group the group ({@code null} not permitted). 193 * 194 * @return The key count. 195 */ 196 public int getKeyCount(Comparable group) { 197 Args.nullNotPermitted(group, "group"); 198 int result = 0; 199 Iterator iterator = this.keyToGroupMap.values().iterator(); 200 while (iterator.hasNext()) { 201 Comparable g = (Comparable) iterator.next(); 202 if (group.equals(g)) { 203 result++; 204 } 205 } 206 return result; 207 } 208 209 /** 210 * Tests the map for equality against an arbitrary object. 211 * 212 * @param obj the object to test against ({@code null} permitted). 213 * 214 * @return A boolean. 215 */ 216 @Override 217 public boolean equals(Object obj) { 218 if (obj == this) { 219 return true; 220 } 221 if (!(obj instanceof KeyToGroupMap)) { 222 return false; 223 } 224 KeyToGroupMap that = (KeyToGroupMap) obj; 225 if (!Objects.equals(this.defaultGroup, that.defaultGroup)) { 226 return false; 227 } 228 if (!this.keyToGroupMap.equals(that.keyToGroupMap)) { 229 return false; 230 } 231 return true; 232 } 233 234 /** 235 * Returns a clone of the map. 236 * 237 * @return A clone. 238 * 239 * @throws CloneNotSupportedException if there is a problem cloning the 240 * map. 241 */ 242 @Override 243 public Object clone() throws CloneNotSupportedException { 244 KeyToGroupMap result = (KeyToGroupMap) super.clone(); 245 result.defaultGroup 246 = (Comparable) KeyToGroupMap.clone(this.defaultGroup); 247 result.groups = (List) KeyToGroupMap.clone(this.groups); 248 result.keyToGroupMap = (Map) KeyToGroupMap.clone(this.keyToGroupMap); 249 return result; 250 } 251 252 /** 253 * Attempts to clone the specified object using reflection. 254 * 255 * @param object the object ({@code null} permitted). 256 * 257 * @return The cloned object, or the original object if cloning failed. 258 */ 259 private static Object clone(Object object) { 260 if (object == null) { 261 return null; 262 } 263 Class c = object.getClass(); 264 Object result = null; 265 try { 266 Method m = c.getMethod("clone", (Class[]) null); 267 if (Modifier.isPublic(m.getModifiers())) { 268 try { 269 result = m.invoke(object, (Object[]) null); 270 } 271 catch (Exception e) { 272 e.printStackTrace(); 273 } 274 } 275 } 276 catch (NoSuchMethodException e) { 277 result = object; 278 } 279 return result; 280 } 281 282 /** 283 * Returns a clone of the list. 284 * 285 * @param list the list. 286 * 287 * @return A clone of the list. 288 * 289 * @throws CloneNotSupportedException if the list could not be cloned. 290 */ 291 private static Collection clone(Collection list) 292 throws CloneNotSupportedException { 293 Collection result = null; 294 if (list != null) { 295 try { 296 List clone = (List) list.getClass().newInstance(); 297 Iterator iterator = list.iterator(); 298 while (iterator.hasNext()) { 299 clone.add(KeyToGroupMap.clone(iterator.next())); 300 } 301 result = clone; 302 } 303 catch (Exception e) { 304 throw new CloneNotSupportedException("Exception."); 305 } 306 } 307 return result; 308 } 309 310}