Frames | No Frames |
1: /* TabularDataSupport.java -- Tables of composite data structures. 2: Copyright (C) 2006, 2007 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package javax.management.openmbean; 39: 40: import java.io.Serializable; 41: 42: import java.util.ArrayList; 43: import java.util.Collection; 44: import java.util.HashMap; 45: import java.util.Iterator; 46: import java.util.List; 47: import java.util.Map; 48: import java.util.Set; 49: 50: /** 51: * Provides an implementation of the {@link TabularData} 52: * interface using a {@link java.util.HashMap}. 53: * 54: * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 55: * @since 1.5 56: */ 57: public class TabularDataSupport 58: implements TabularData, Serializable, Cloneable, Map<Object,Object> 59: { 60: 61: /** 62: * Compatible with JDK 1.5 63: */ 64: private static final long serialVersionUID = 5720150593236309827L; 65: 66: /** 67: * Mapping of rows to column values. 68: * 69: * @serial the map of rows to column values. 70: */ 71: private HashMap<Object,Object> dataMap; 72: 73: /** 74: * The tabular type which represents this tabular data instance. 75: * 76: * @serial the type information for this instance. 77: */ 78: private TabularType tabularType; 79: 80: /** 81: * Constructs a new empty {@link TabularDataSupport} with the 82: * specified type. The type may not be null. This constructor 83: * simply calls the other, with the default initial capacity of 84: * <code>101</code> and default load factor of <code>0.75</code>. 85: * 86: * @param type the tabular type of this tabular data instance. 87: * @throws IllegalArgumentException if <code>type</code> is 88: * <code>null</code>. 89: */ 90: public TabularDataSupport(TabularType type) 91: { 92: this(type, 101, 0.75f); 93: } 94: 95: /** 96: * Constructs a new empty {@link TabularDataSupport} with the 97: * specified type and the supplied initial capacity and load factor 98: * being used for the underlying {@link java.util.HashMap}. The 99: * type may not be null and the initial capacity and load factor 100: * must be positive. 101: * 102: * @param type the tabular type of this tabular data instance. 103: * @param cap the initial capacity of the underlying map. 104: * @param lf the load factor of the underlying map. 105: * @throws IllegalArgumentException if <code>type</code> is 106: * <code>null</code>, or 107: * <code>cap</code> or 108: * <code>lf</code> are 109: * negative. 110: */ 111: public TabularDataSupport(TabularType type, int cap, float lf) 112: { 113: if (type == null) 114: throw new IllegalArgumentException("The type may not be null."); 115: tabularType = type; 116: dataMap = new HashMap<Object,Object>(cap, lf); 117: } 118: 119: /** 120: * Calculates the index the specified {@link CompositeData} value 121: * would have, if it was to be added to this {@link TabularData} 122: * instance. This method includes a check that the type of the 123: * given value is the same as the row type of this instance, but not 124: * a check for existing instances of the given value. The value 125: * must also not be <code>null</code>. Possible indices are 126: * selected by the {@link TabularType#getIndexNames()} method of 127: * this instance's tabular type. The returned indices are the 128: * values of the fields in the supplied {@link CompositeData} 129: * instance that match the names given in the {@link TabularType}. 130: * 131: * @param val the {@link CompositeData} value whose index should 132: * be calculated. 133: * @return the index the value would take on, if it were to be added. 134: * @throws NullPointerException if the value is <code>null</code>. 135: * @throws InvalidOpenTypeException if the value does not match the 136: * row type of this instance. 137: */ 138: public Object[] calculateIndex(CompositeData val) 139: { 140: if (!(val.getCompositeType().equals(tabularType.getRowType()))) 141: throw new InvalidOpenTypeException("The type of the given value " + 142: "does not match the row type " + 143: "of this instance."); 144: List<String> indexNames = tabularType.getIndexNames(); 145: List<String> matchingIndicies = new ArrayList<String>(indexNames.size()); 146: for (String name : indexNames) 147: matchingIndicies.add(val.get(name).toString()); 148: return matchingIndicies.toArray(); 149: } 150: 151: /** 152: * Removes all {@link CompositeData} values from the table. 153: */ 154: public void clear() 155: { 156: dataMap.clear(); 157: } 158: 159: /** 160: * Returns a shallow clone of the information, as obtained by the 161: * {@link Object} implementation of {@link Object#clone()}. The map 162: * is also cloned, but it still references the same objects. 163: * 164: * @return a shallow clone of this {@link TabularDataSupport}. 165: */ 166: @SuppressWarnings("unchecked") 167: public Object clone() 168: { 169: TabularDataSupport clone = null; 170: try 171: { 172: clone = (TabularDataSupport) super.clone(); 173: clone.setMap((HashMap<Object,Object>) dataMap.clone()); 174: } 175: catch (CloneNotSupportedException e) 176: { 177: /* This won't happen as we implement Cloneable */ 178: } 179: return clone; 180: } 181: 182: /** 183: * Returns true iff this instance of the {@link TabularData} class 184: * contains a {@link CompositeData} value at the specified index. 185: * The method returns <code>false</code> if the given key can 186: * not be cast to an {@link java.lang.Object} array; otherwise 187: * it returns the result of {@link #containsKey(java.lang.Object[])}. 188: * 189: * 190: * @param key the key to test for. 191: * @return true if the key maps to a {@link CompositeData} value. 192: */ 193: public boolean containsKey(Object key) 194: { 195: if (key instanceof Object[]) 196: return containsKey((Object[]) key); 197: else 198: return false; 199: } 200: 201: /** 202: * Returns true iff this instance of the {@link TabularData} class 203: * contains a {@link CompositeData} value at the specified index. 204: * In any other circumstance, including if the given key 205: * is <code>null</code> or of the incorrect type, according to 206: * the {@link TabularType} of this instance, this method returns 207: * false. 208: * 209: * @param key the key to test for. 210: * @return true if the key maps to a {@link CompositeData} value. 211: */ 212: public boolean containsKey(Object[] key) 213: { 214: if (key == null) 215: return false; 216: if (!(isKeyValid(key))) 217: return false; 218: return dataMap.containsKey(key); 219: } 220: 221: /** 222: * Returns true iff this instance of the {@link TabularData} class 223: * contains the specified {@link CompositeData} value. If the given 224: * value is not an instance of {@link CompositeData}, this method 225: * simply returns false. 226: * 227: * @param val the value to test for. 228: * @return true if the value exists. 229: */ 230: public boolean containsValue(Object val) 231: { 232: if (val instanceof CompositeData) 233: return containsValue((CompositeData) val); 234: else 235: return false; 236: } 237: 238: /** 239: * Returns true iff this instance of the {@link TabularData} class 240: * contains the specified {@link CompositeData} value. 241: * In any other circumstance, including if the given value 242: * is <code>null</code> or of the incorrect type, according to 243: * the {@link TabularType} of this instance, this method returns 244: * false. 245: * 246: * @param val the value to test for. 247: * @return true if the value exists. 248: */ 249: public boolean containsValue(CompositeData val) 250: { 251: if (val == null) 252: return false; 253: if (!(val.getCompositeType().equals(tabularType.getRowType()))) 254: return false; 255: return dataMap.containsValue(val); 256: } 257: 258: /** 259: * <p> 260: * Returns a set view of the mappings in this Map. Each element in the 261: * set is a Map.Entry. The set is backed by the map, so that changes in 262: * one show up in the other. Modifications made while an iterator is 263: * in progress cause undefined behavior. If the set supports removal, 264: * these methods remove the underlying mapping from the map: 265: * <code>Iterator.remove</code>, <code>Set.remove</code>, 266: * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code>. 267: * Element addition, via <code>add</code> or <code>addAll</code>, is 268: * not supported via this set. 269: * </p> 270: * <p> 271: * <strong>Note</strong>: using the 272: * {@link java.util.Map.Entry#setValue(Object) will cause corruption of 273: * the index to row mappings. 274: * </p> 275: * 276: * @return the set view of all mapping entries 277: * @see java.util.Map.Entry 278: */ 279: public Set<Map.Entry<Object,Object>> entrySet() 280: { 281: return dataMap.entrySet(); 282: } 283: 284: /** 285: * Compares the specified object with this object for equality. 286: * The object is judged equivalent if it is non-null, and also 287: * an instance of {@link TabularData} with the same row type, 288: * and {@link CompositeData} values. The two compared instances may 289: * be equivalent even if they represent different implementations 290: * of {@link TabularData}. 291: * 292: * @param obj the object to compare for equality. 293: * @return true if <code>obj</code> is equal to <code>this</code>. 294: */ 295: public boolean equals(Object obj) 296: { 297: if (!(obj instanceof TabularData)) 298: return false; 299: TabularData data = (TabularData) obj; 300: return tabularType.equals(data.getTabularType()) && 301: dataMap.values().equals(data.values()); 302: } 303: 304: /** 305: * Retrieves the value for the specified key by simply 306: * calling <code>get((Object[]) key)</code>. 307: * 308: * @param key the key whose value should be returned. 309: * @return the matching {@link CompositeData} value, or 310: * <code>null</code> if one does not exist. 311: * @throws NullPointerException if the key is <code>null</code>. 312: * @throws ClassCastException if the key is not an instance 313: * of <code>Object[]</code>. 314: * @throws InvalidKeyException if the key does not match 315: * the {@link TabularType} of this 316: * instance. 317: */ 318: public Object get(Object key) 319: { 320: return get((Object[]) key); 321: } 322: 323: /** 324: * Retrieves the {@link CompositeData} value for the specified 325: * key, or <code>null</code> if no such mapping exists. 326: * 327: * @param key the key whose value should be returned. 328: * @return the matching {@link CompositeData} value, or 329: * <code>null</code> if one does not exist. 330: * @throws NullPointerException if the key is <code>null</code>. 331: * @throws InvalidKeyException if the key does not match 332: * the {@link TabularType} of this 333: * instance. 334: */ 335: public CompositeData get(Object[] key) 336: { 337: if (!(isKeyValid(key))) 338: throw new InvalidKeyException("The key does not match the " + 339: "tabular type of this instance."); 340: return (CompositeData) dataMap.get(key); 341: } 342: 343: /** 344: * Returns the tabular type which corresponds to this instance 345: * of {@link TabularData}. 346: * 347: * @return the tabular type for this instance. 348: */ 349: public TabularType getTabularType() 350: { 351: return tabularType; 352: } 353: 354: /** 355: * Returns the hash code of the composite data type. This is 356: * computed as the sum of the hash codes of each value, together 357: * with the hash code of the tabular type. These are the same 358: * elements of the type that are compared as part of the {@link 359: * #equals(java.lang.Object)} method, thus ensuring that the 360: * hashcode is compatible with the equality test. 361: * 362: * @return the hash code of this instance. 363: */ 364: public int hashCode() 365: { 366: return tabularType.hashCode() + dataMap.values().hashCode(); 367: } 368: 369: /** 370: * Returns true if this {@link TabularData} instance 371: * contains no {@link CompositeData} values. 372: * 373: * @return true if the instance is devoid of rows. 374: */ 375: public boolean isEmpty() 376: { 377: return dataMap.isEmpty(); 378: } 379: 380: /** 381: * Returns true if the given key is valid for the 382: * @link{TabularType} of this instance. 383: * 384: * @return true if the key is valid. 385: * @throws NullPointerException if <code>key</code> 386: * is null. 387: */ 388: private boolean isKeyValid(Object[] key) 389: { 390: Iterator<String> it = tabularType.getIndexNames().iterator(); 391: CompositeType rowType = tabularType.getRowType(); 392: for (int a = 0; it.hasNext(); ++a) 393: { 394: OpenType<?> type = rowType.getType(it.next()); 395: if (!(type.isValue(key[a]))) 396: return false; 397: } 398: return true; 399: } 400: 401: /** 402: * Returns a set view of the keys in this Map. The set is backed by the 403: * map, so that changes in one show up in the other. Modifications made 404: * while an iterator is in progress cause undefined behavior. If the set 405: * supports removal, these methods remove the underlying mapping from 406: * the map: <code>Iterator.remove</code>, <code>Set.remove</code>, 407: * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code>. 408: * Element addition, via <code>add</code> or <code>addAll</code>, is 409: * not supported via this set. 410: * 411: * @return the set view of all keys 412: */ 413: public Set<Object> keySet() 414: { 415: return dataMap.keySet(); 416: } 417: 418: /** 419: * Adds the specified {@link CompositeData} value to the 420: * table. The value must be non-null, of the same type 421: * as the row type of this instance, and must not have 422: * the same index as an existing value. The index is 423: * calculated using the index names of the 424: * {@link TabularType} for this instance. 425: * 426: * @param val the {@link CompositeData} value to add. 427: * @throws NullPointerException if <code>val</code> is 428: * <code>null</code>. 429: * @throws InvalidOpenTypeException if the type of the 430: * given value does not 431: * match the row type. 432: * @throws KeyAlreadyExistsException if the value has the 433: * same calculated index 434: * as an existing value. 435: */ 436: public void put(CompositeData val) 437: { 438: Object[] key = calculateIndex(val); 439: if (dataMap.containsKey(key)) 440: throw new KeyAlreadyExistsException("A value with this index " + 441: "already exists."); 442: dataMap.put(key, val); 443: } 444: 445: /** 446: * Adds the specified {@link CompositeData} value to the 447: * table, ignoring the supplied key, by simply calling 448: * <code>put((CompositeData) val)</code>. 449: * 450: * @param key ignored. 451: * @param val the {@link CompositeData} value to add. 452: * @return the {@link CompositeData} value. 453: * @throws NullPointerException if <code>val</code> is 454: * <code>null</code>. 455: * @throws InvalidOpenTypeException if the type of the 456: * given value does not 457: * match the row type. 458: * @throws KeyAlreadyExistsException if the value has the 459: * same calculated index 460: * as an existing value. 461: */ 462: public Object put(Object key, Object val) 463: { 464: put((CompositeData) val); 465: return val; 466: } 467: 468: /** 469: * Adds each of the specified {@link CompositeData} values 470: * to the table. Each element of the array must meet the 471: * conditions given for the {@link #put(CompositeData)} 472: * method. In addition, the index of each value in the 473: * array must be distinct from the index of the other 474: * values in the array, as well as from the existing values 475: * in the table. The operation should be atomic; if one 476: * value can not be added, then none of the values should 477: * be. If the array is <code>null</code> or empty, the 478: * method simply returns. 479: * 480: * @param vals the {@link CompositeData} values to add. 481: * @throws NullPointerException if a value from the array is 482: * <code>null</code>. 483: * @throws InvalidOpenTypeException if the type of a 484: * given value does not 485: * match the row type. 486: * @throws KeyAlreadyExistsException if a value has the 487: * same calculated index 488: * as an existing value or 489: * of one of the other 490: * specified values. 491: */ 492: public void putAll(CompositeData[] vals) 493: { 494: if (vals == null || vals.length == 0) 495: return; 496: Map<Object,Object> mapToAdd = new HashMap<Object,Object>(vals.length); 497: for (int a = 0; a < vals.length; ++a) 498: { 499: Object[] key = calculateIndex(vals[a]); 500: if (dataMap.containsKey(key)) 501: throw new KeyAlreadyExistsException("Element " + a + ": A " + 502: "value with this index " + 503: "already exists."); 504: mapToAdd.put(key, vals[a]); 505: } 506: dataMap.putAll(mapToAdd); 507: } 508: 509: /** 510: * Converts each value from the specified map to a member of an 511: * array of {@link CompositeData} values and adds them using {@link 512: * #put(CompositeData[])}, if possible. As in {@link 513: * #put(Object,Object)}, the keys are simply ignored. This method 514: * is useful for adding the {@link CompositeData} values from a 515: * different {@link TabularData} instance, which uses the same 516: * {@link TabularType} but a different selection of index names, to 517: * this one. If the map is <code>null</code> or empty, the method 518: * simply returns. 519: * 520: * @param m the map to add. Only the values are used and must 521: * all be instances of {@link CompositeData}. 522: * @throws NullPointerException if a value from the map is 523: * <code>null</code>. 524: * @throws ClassCastException if a value from the map is not 525: * an instance of {@link CompositeData}. 526: * @throws InvalidOpenTypeException if the type of the 527: * given value does not 528: * match the row type. 529: * @throws KeyAlreadyExistsException if the value has the 530: * same calculated index 531: * as an existing value or 532: * of one of the other 533: * specified values. 534: */ 535: public void putAll(Map<?,?> m) 536: { 537: if (m == null || m.size() == 0) 538: return; 539: Collection<?> vals = m.values(); 540: CompositeData[] data = new CompositeData[vals.size()]; 541: Iterator<?> it = vals.iterator(); 542: for (int a = 0; it.hasNext(); ++a) 543: { 544: data[a] = (CompositeData) it.next(); 545: } 546: putAll(data); 547: } 548: 549: /** 550: * Removes the value for the specified key by simply 551: * calling <code>remove((Object[]) key)</code>. 552: * 553: * @param key the key whose value should be removed. 554: * @return the removed value, or <code>null</code> if 555: * there is no value for the given key. 556: * @throws NullPointerException if the key is <code>null</code>. 557: * @throws ClassCastException if the key is not an instance 558: * of <code>Object[]</code>. 559: * @throws InvalidOpenTypeException if the key does not match 560: * the {@link TabularType} of this 561: * instance. 562: */ 563: public Object remove(Object key) 564: { 565: return remove((Object[]) key); 566: } 567: 568: /** 569: * Removes the {@link CompositeData} value located at the 570: * specified index. <code>null</code> is returned if the 571: * value does not exist. Otherwise, the removed value is 572: * returned. 573: * 574: * @param key the key of the value to remove. 575: * @return the removed value, or <code>null</code> if 576: * there is no value for the given key. 577: * @throws NullPointerException if the key is <code>null</code>. 578: * @throws InvalidOpenTypeException if the key does not match 579: * the {@link TabularType} of this 580: * instance. 581: */ 582: public CompositeData remove(Object[] key) 583: { 584: if (!(isKeyValid(key))) 585: throw new InvalidKeyException("The key does not match the " + 586: "tabular type of this instance."); 587: return (CompositeData) dataMap.remove(key); 588: } 589: 590: /** 591: * Private method to set the internal {@link java.util.Map} 592: * instance (used in cloning). 593: * 594: * @param map the new map used. 595: */ 596: private void setMap(HashMap<Object,Object> map) 597: { 598: dataMap = map; 599: } 600: 601: /** 602: * Returns the number of {@link CompositeData} values or rows 603: * in the table. 604: * 605: * @return the number of rows in the table. 606: */ 607: public int size() 608: { 609: return dataMap.size(); 610: } 611: 612: /** 613: * Returns a textual representation of this instance. This 614: * is constructed using the class name 615: * (<code>javax.management.openmbean.TabularDataSupport</code>) 616: * and the result of calling <code>toString()</code> on the 617: * tabular type and underlying hash map instance. 618: * 619: * @return a {@link java.lang.String} representation of the 620: * object. 621: */ 622: public String toString() 623: { 624: return getClass().getName() 625: + "[tabularType=" + tabularType 626: + ",dataMap=" + dataMap 627: + "]"; 628: } 629: 630: /** 631: * Returns a collection (or bag) view of the values in this Map. The 632: * collection is backed by the map, so that changes in one show up in 633: * the other. Modifications made while an iterator is in progress cause 634: * undefined behavior. If the collection supports removal, these methods 635: * remove the underlying mapping from the map: <code>Iterator.remove</code>, 636: * <code>Collection.remove</code>, <code>removeAll</code>, 637: * <code>retainAll</code>, and <code>clear</code>. Element addition, via 638: * <code>add</code> or <code>addAll</code>, is not supported via this 639: * collection. 640: * 641: * @return the collection view of all values 642: */ 643: public Collection<Object> values() 644: { 645: return dataMap.values(); 646: } 647: 648: }