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 under the terms
  10. * of the GNU Lesser General Public License as published by the Free Software Foundation;
  11. * either version 2.1 of the License, or (at your option) any later version.
  12. *
  13. * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
  14. * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  15. * See the GNU Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public License along with this
  18. * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  19. * Boston, MA 02111-1307, USA.
  20. *
  21. * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
  22. * in the United States and other countries.]
  23. *
  24. * ------------------
  25. * KeyToGroupMap.java
  26. * ------------------
  27. * (C) Copyright 2004, 2005, by Object Refinery Limited and Contributors.
  28. *
  29. * Original Author: David Gilbert (for Object Refinery Limited);
  30. * Contributor(s): -;
  31. *
  32. * $Id: KeyToGroupMap.java,v 1.4 2005/02/02 17:55:45 mungady Exp $
  33. *
  34. * Changes
  35. * -------
  36. * 29-Apr-2004 : Version 1 (DG);
  37. * 07-Jul-2004 : Added a group list to ensure group index is consistent, fixed cloning
  38. * problem (DG);
  39. *
  40. */
  41. package org.jfree.data;
  42. import java.io.Serializable;
  43. import java.lang.reflect.Method;
  44. import java.lang.reflect.Modifier;
  45. import java.util.ArrayList;
  46. import java.util.Collection;
  47. import java.util.HashMap;
  48. import java.util.Iterator;
  49. import java.util.List;
  50. import java.util.Map;
  51. import org.jfree.util.ObjectUtilities;
  52. import org.jfree.util.PublicCloneable;
  53. /**
  54. * A class that maps keys (instances of <code>Comparable</code>) to groups.
  55. */
  56. public class KeyToGroupMap implements Cloneable, PublicCloneable, Serializable {
  57. /** The default group. */
  58. private Comparable defaultGroup;
  59. /** The groups. */
  60. private List groups;
  61. /** A mapping between keys and groups. */
  62. private Map keyToGroupMap;
  63. /**
  64. * Creates a new map with a default group named 'Default Group'.
  65. */
  66. public KeyToGroupMap() {
  67. this("Default Group");
  68. }
  69. /**
  70. * Creates a new map with the specified default group.
  71. *
  72. * @param defaultGroup the default group (<code>null</code> not permitted).
  73. */
  74. public KeyToGroupMap(Comparable defaultGroup) {
  75. if (defaultGroup == null) {
  76. throw new IllegalArgumentException("Null 'defaultGroup' argument.");
  77. }
  78. this.defaultGroup = defaultGroup;
  79. this.groups = new ArrayList();
  80. this.keyToGroupMap = new HashMap();
  81. }
  82. /**
  83. * Returns the number of groups in the map.
  84. *
  85. * @return The number of groups in the map.
  86. */
  87. public int getGroupCount() {
  88. return this.groups.size() + 1;
  89. }
  90. /**
  91. * Returns a list of the groups (always including the default group) in the map. The
  92. * returned list is independent of the map, so altering the list will have no effect.
  93. *
  94. * @return The groups (never <code>null</code>).
  95. */
  96. public List getGroups() {
  97. List result = new ArrayList();
  98. result.add(this.defaultGroup);
  99. Iterator iterator = this.groups.iterator();
  100. while (iterator.hasNext()) {
  101. Comparable group = (Comparable) iterator.next();
  102. if (!result.contains(group)) {
  103. result.add(group);
  104. }
  105. }
  106. return result;
  107. }
  108. /**
  109. * Returns the index for the group.
  110. *
  111. * @param group the group.
  112. *
  113. * @return The group index (or -1 if the group is not represented within the map).
  114. */
  115. public int getGroupIndex(Comparable group) {
  116. int result = this.groups.indexOf(group);
  117. if (result < 0) {
  118. if (this.defaultGroup.equals(group)) {
  119. result = 0;
  120. }
  121. }
  122. else {
  123. result = result + 1;
  124. }
  125. return result;
  126. }
  127. /**
  128. * Returns the group that a key is mapped to.
  129. *
  130. * @param key the key (<code>null</code> not permitted).
  131. *
  132. * @return The group (never <code>null</code>, returns the default group if there
  133. * is no mapping for the specified key).
  134. */
  135. public Comparable getGroup(Comparable key) {
  136. if (key == null) {
  137. throw new IllegalArgumentException("Null 'key' argument.");
  138. }
  139. Comparable result = this.defaultGroup;
  140. Comparable group = (Comparable) this.keyToGroupMap.get(key);
  141. if (group != null) {
  142. result = group;
  143. }
  144. return result;
  145. }
  146. /**
  147. * Maps a key to a group.
  148. *
  149. * @param key the key (<code>null</code> not permitted).
  150. * @param group the group (<code>null</code> permitted, clears any existing mapping).
  151. */
  152. public void mapKeyToGroup(Comparable key, Comparable group) {
  153. if (key == null) {
  154. throw new IllegalArgumentException("Null 'key' argument.");
  155. }
  156. Comparable currentGroup = getGroup(key);
  157. if (!currentGroup.equals(this.defaultGroup)) {
  158. if (!currentGroup.equals(group)) {
  159. int count = getKeyCount(currentGroup);
  160. if (count == 1) {
  161. this.groups.remove(currentGroup);
  162. }
  163. }
  164. }
  165. if (group == null) {
  166. this.keyToGroupMap.remove(key);
  167. }
  168. else {
  169. if (!this.groups.contains(group)) {
  170. if (!this.defaultGroup.equals(group)) {
  171. this.groups.add(group);
  172. }
  173. }
  174. this.keyToGroupMap.put(key, group);
  175. }
  176. }
  177. /**
  178. * Returns the number of keys mapped to the specified group. This method won't
  179. * always return an accurate result for the default group, since explicit mappings
  180. * are not required for this group.
  181. *
  182. * @param group the group (<code>null</code> not permitted).
  183. *
  184. * @return The key count.
  185. */
  186. public int getKeyCount(Comparable group) {
  187. if (group == null) {
  188. throw new IllegalArgumentException("Null 'group' argument.");
  189. }
  190. int result = 0;
  191. Iterator iterator = this.keyToGroupMap.values().iterator();
  192. while (iterator.hasNext()) {
  193. Comparable g = (Comparable) iterator.next();
  194. if (group.equals(g)) {
  195. result++;
  196. }
  197. }
  198. return result;
  199. }
  200. /**
  201. * Tests the map for equality against an arbitrary object.
  202. *
  203. * @param obj the object to test against (<code>null</code> permitted).
  204. *
  205. * @return A boolean.
  206. */
  207. public boolean equals(Object obj) {
  208. if (obj == this) {
  209. return true;
  210. }
  211. if (!(obj instanceof KeyToGroupMap)) {
  212. return false;
  213. }
  214. KeyToGroupMap that = (KeyToGroupMap) obj;
  215. if (!ObjectUtilities.equal(this.defaultGroup, that.defaultGroup)) {
  216. return false;
  217. }
  218. if (!this.keyToGroupMap.equals(that.keyToGroupMap)) {
  219. return false;
  220. }
  221. return true;
  222. }
  223. /**
  224. * Returns a clone of the map.
  225. *
  226. * @return A clone.
  227. *
  228. * @throws CloneNotSupportedException if there is a problem cloning the map.
  229. */
  230. public Object clone() throws CloneNotSupportedException {
  231. KeyToGroupMap result = (KeyToGroupMap) super.clone();
  232. result.defaultGroup = (Comparable) KeyToGroupMap.clone(this.defaultGroup);
  233. result.groups = (List) KeyToGroupMap.clone(this.groups);
  234. result.keyToGroupMap = (Map) KeyToGroupMap.clone(this.keyToGroupMap);
  235. return result;
  236. }
  237. /**
  238. * Attempts to clone the specified object using reflection.
  239. *
  240. * @param object the object (<code>null</code> permitted).
  241. *
  242. * @return The cloned object, or the original object if cloning failed.
  243. */
  244. private static Object clone(Object object) {
  245. if (object == null) {
  246. return null;
  247. }
  248. Class c = object.getClass();
  249. Object result = null;
  250. try {
  251. Method m = c.getMethod("clone", null);
  252. if (Modifier.isPublic(m.getModifiers())) {
  253. try {
  254. result = m.invoke(object, null);
  255. }
  256. catch (Exception e) {
  257. e.printStackTrace();
  258. }
  259. }
  260. }
  261. catch (NoSuchMethodException e) {
  262. result = object;
  263. }
  264. return result;
  265. }
  266. /**
  267. * Returns a clone of the list.
  268. *
  269. * @param list the list.
  270. *
  271. * @return A clone of the list.
  272. *
  273. * @throws CloneNotSupportedException if the list could not be cloned.
  274. */
  275. private static Collection clone(Collection list) throws CloneNotSupportedException {
  276. Collection result = null;
  277. if (list != null) {
  278. try {
  279. List clone = (List) list.getClass().newInstance();
  280. Iterator iterator = list.iterator();
  281. while (iterator.hasNext()) {
  282. clone.add(KeyToGroupMap.clone(iterator.next()));
  283. }
  284. result = clone;
  285. }
  286. catch (Exception e) {
  287. throw new CloneNotSupportedException("Exception.");
  288. }
  289. }
  290. return result;
  291. }
  292. }