GNU Classpath (0.95) | |
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 Map<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 indexNames = tabularType.getIndexNames(); 145: List matchingIndicies = new ArrayList(indexNames.size()); 146: Iterator it = indexNames.iterator(); 147: while (it.hasNext()) 148: { 149: String name = (String) it.next(); 150: matchingIndicies.add(val.get(name)); 151: } 152: return matchingIndicies.toArray(); 153: } 154: 155: /** 156: * Removes all {@link CompositeData} values from the table. 157: */ 158: public void clear() 159: { 160: dataMap.clear(); 161: } 162: 163: /** 164: * Returns a shallow clone of the information, as obtained by the 165: * {@link Object} implementation of {@link Object#clone()}. The map 166: * is also cloned, but it still references the same objects. 167: * 168: * @return a shallow clone of this {@link TabularDataSupport}. 169: */ 170: public Object clone() 171: { 172: TabularDataSupport clone = null; 173: try 174: { 175: clone = (TabularDataSupport) super.clone(); 176: clone.setMap((HashMap) ((HashMap) dataMap).clone()); 177: } 178: catch (CloneNotSupportedException e) 179: { 180: /* This won't happen as we implement Cloneable */ 181: } 182: return clone; 183: } 184: 185: /** 186: * Returns true iff this instance of the {@link TabularData} class 187: * contains a {@link CompositeData} value at the specified index. 188: * The method returns <code>false</code> if the given key can 189: * not be cast to an {@link java.lang.Object} array; otherwise 190: * it returns the result of {@link #containsKey(java.lang.Object[])}. 191: * 192: * 193: * @param key the key to test for. 194: * @return true if the key maps to a {@link CompositeData} value. 195: */ 196: public boolean containsKey(Object key) 197: { 198: if (key instanceof Object[]) 199: return containsKey((Object[]) key); 200: else 201: return false; 202: } 203: 204: /** 205: * Returns true iff this instance of the {@link TabularData} class 206: * contains a {@link CompositeData} value at the specified index. 207: * In any other circumstance, including if the given key 208: * is <code>null</code> or of the incorrect type, according to 209: * the {@link TabularType} of this instance, this method returns 210: * false. 211: * 212: * @param key the key to test for. 213: * @return true if the key maps to a {@link CompositeData} value. 214: */ 215: public boolean containsKey(Object[] key) 216: { 217: if (key == null) 218: return false; 219: if (!(isKeyValid(key))) 220: return false; 221: return dataMap.containsKey(key); 222: } 223: 224: /** 225: * Returns true iff this instance of the {@link TabularData} class 226: * contains the specified {@link CompositeData} value. If the given 227: * value is not an instance of {@link CompositeData}, this method 228: * simply returns false. 229: * 230: * @param val the value to test for. 231: * @return true if the value exists. 232: */ 233: public boolean containsValue(Object val) 234: { 235: if (val instanceof CompositeData) 236: return containsValue((CompositeData) val); 237: else 238: return false; 239: } 240: 241: /** 242: * Returns true iff this instance of the {@link TabularData} class 243: * contains the specified {@link CompositeData} value. 244: * In any other circumstance, including if the given value 245: * is <code>null</code> or of the incorrect type, according to 246: * the {@link TabularType} of this instance, this method returns 247: * false. 248: * 249: * @param val the value to test for. 250: * @return true if the value exists. 251: */ 252: public boolean containsValue(CompositeData val) 253: { 254: if (val == null) 255: return false; 256: if (!(val.getCompositeType().equals(tabularType.getRowType()))) 257: return false; 258: return dataMap.containsValue(val); 259: } 260: 261: /** 262: * <p> 263: * Returns a set view of the mappings in this Map. Each element in the 264: * set is a Map.Entry. The set is backed by the map, so that changes in 265: * one show up in the other. Modifications made while an iterator is 266: * in progress cause undefined behavior. If the set supports removal, 267: * these methods remove the underlying mapping from the map: 268: * <code>Iterator.remove</code>, <code>Set.remove</code>, 269: * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code>. 270: * Element addition, via <code>add</code> or <code>addAll</code>, is 271: * not supported via this set. 272: * </p> 273: * <p> 274: * <strong>Note</strong>: using the 275: * {@link java.util.Map.Entry#setValue(Object) will cause corruption of 276: * the index to row mappings. 277: * </p> 278: * 279: * @return the set view of all mapping entries 280: * @see java.util.Map.Entry 281: */ 282: public Set<Map.Entry<Object,Object>> entrySet() 283: { 284: return dataMap.entrySet(); 285: } 286: 287: /** 288: * Compares the specified object with this object for equality. 289: * The object is judged equivalent if it is non-null, and also 290: * an instance of {@link TabularData} with the same row type, 291: * and {@link CompositeData} values. The two compared instances may 292: * be equivalent even if they represent different implementations 293: * of {@link TabularData}. 294: * 295: * @param obj the object to compare for equality. 296: * @return true if <code>obj</code> is equal to <code>this</code>. 297: */ 298: public boolean equals(Object obj) 299: { 300: if (!(obj instanceof TabularData)) 301: return false; 302: TabularData data = (TabularData) obj; 303: return tabularType.equals(data.getTabularType()) && 304: dataMap.values().equals(data.values()); 305: } 306: 307: /** 308: * Retrieves the value for the specified key by simply 309: * calling <code>get((Object[]) key)</code>. 310: * 311: * @param key the key whose value should be returned. 312: * @return the matching {@link CompositeData} value, or 313: * <code>null</code> if one does not exist. 314: * @throws NullPointerException if the key is <code>null</code>. 315: * @throws ClassCastException if the key is not an instance 316: * of <code>Object[]</code>. 317: * @throws InvalidKeyException if the key does not match 318: * the {@link TabularType} of this 319: * instance. 320: */ 321: public Object get(Object key) 322: { 323: return get((Object[]) key); 324: } 325: 326: /** 327: * Retrieves the {@link CompositeData} value for the specified 328: * key, or <code>null</code> if no such mapping exists. 329: * 330: * @param key the key whose value should be returned. 331: * @return the matching {@link CompositeData} value, or 332: * <code>null</code> if one does not exist. 333: * @throws NullPointerException if the key is <code>null</code>. 334: * @throws InvalidKeyException if the key does not match 335: * the {@link TabularType} of this 336: * instance. 337: */ 338: public CompositeData get(Object[] key) 339: { 340: if (!(isKeyValid(key))) 341: throw new InvalidKeyException("The key does not match the " + 342: "tabular type of this instance."); 343: return (CompositeData) dataMap.get(key); 344: } 345: 346: /** 347: * Returns the tabular type which corresponds to this instance 348: * of {@link TabularData}. 349: * 350: * @return the tabular type for this instance. 351: */ 352: public TabularType getTabularType() 353: { 354: return tabularType; 355: } 356: 357: /** 358: * Returns the hash code of the composite data type. This is 359: * computed as the sum of the hash codes of each value, together 360: * with the hash code of the tabular type. These are the same 361: * elements of the type that are compared as part of the {@link 362: * #equals(java.lang.Object)} method, thus ensuring that the 363: * hashcode is compatible with the equality test. 364: * 365: * @return the hash code of this instance. 366: */ 367: public int hashCode() 368: { 369: return tabularType.hashCode() + dataMap.values().hashCode(); 370: } 371: 372: /** 373: * Returns true if this {@link TabularData} instance 374: * contains no {@link CompositeData} values. 375: * 376: * @return true if the instance is devoid of rows. 377: */ 378: public boolean isEmpty() 379: { 380: return dataMap.isEmpty(); 381: } 382: 383: /** 384: * Returns true if the given key is valid for the 385: * @link{TabularType} of this instance. 386: * 387: * @return true if the key is valid. 388: * @throws NullPointerException if <code>key</code> 389: * is null. 390: */ 391: private boolean isKeyValid(Object[] key) 392: { 393: Iterator it = tabularType.getIndexNames().iterator(); 394: CompositeType rowType = tabularType.getRowType(); 395: for (int a = 0; it.hasNext(); ++a) 396: { 397: OpenType type = rowType.getType((String) it.next()); 398: if (!(type.isValue(key[a]))) 399: return false; 400: } 401: return true; 402: } 403: 404: /** 405: * Returns a set view of the keys in this Map. The set is backed by the 406: * map, so that changes in one show up in the other. Modifications made 407: * while an iterator is in progress cause undefined behavior. If the set 408: * supports removal, these methods remove the underlying mapping from 409: * the map: <code>Iterator.remove</code>, <code>Set.remove</code>, 410: * <code>removeAll</code>, <code>retainAll</code>, and <code>clear</code>. 411: * Element addition, via <code>add</code> or <code>addAll</code>, is 412: * not supported via this set. 413: * 414: * @return the set view of all keys 415: */ 416: public Set<Object> keySet() 417: { 418: return dataMap.keySet(); 419: } 420: 421: /** 422: * Adds the specified {@link CompositeData} value to the 423: * table. The value must be non-null, of the same type 424: * as the row type of this instance, and must not have 425: * the same index as an existing value. The index is 426: * calculated using the index names of the 427: * {@link TabularType} for this instance. 428: * 429: * @param val the {@link CompositeData} value to add. 430: * @throws NullPointerException if <code>val</code> is 431: * <code>null</code>. 432: * @throws InvalidOpenTypeException if the type of the 433: * given value does not 434: * match the row type. 435: * @throws KeyAlreadyExistsException if the value has the 436: * same calculated index 437: * as an existing value. 438: */ 439: public void put(CompositeData val) 440: { 441: Object[] key = calculateIndex(val); 442: if (dataMap.containsKey(key)) 443: throw new KeyAlreadyExistsException("A value with this index " + 444: "already exists."); 445: dataMap.put(key, val); 446: } 447: 448: /** 449: * Adds the specified {@link CompositeData} value to the 450: * table, ignoring the supplied key, by simply calling 451: * <code>put((CompositeData) val)</code>. 452: * 453: * @param key ignored. 454: * @param val the {@link CompositeData} value to add. 455: * @return the {@link CompositeData} value. 456: * @throws NullPointerException if <code>val</code> is 457: * <code>null</code>. 458: * @throws InvalidOpenTypeException if the type of the 459: * given value does not 460: * match the row type. 461: * @throws KeyAlreadyExistsException if the value has the 462: * same calculated index 463: * as an existing value. 464: */ 465: public Object put(Object key, Object val) 466: { 467: put((CompositeData) val); 468: return val; 469: } 470: 471: /** 472: * Adds each of the specified {@link CompositeData} values 473: * to the table. Each element of the array must meet the 474: * conditions given for the {@link #put(CompositeData)} 475: * method. In addition, the index of each value in the 476: * array must be distinct from the index of the other 477: * values in the array, as well as from the existing values 478: * in the table. The operation should be atomic; if one 479: * value can not be added, then none of the values should 480: * be. If the array is <code>null</code> or empty, the 481: * method simply returns. 482: * 483: * @param vals the {@link CompositeData} values to add. 484: * @throws NullPointerException if a value from the array is 485: * <code>null</code>. 486: * @throws InvalidOpenTypeException if the type of a 487: * given value does not 488: * match the row type. 489: * @throws KeyAlreadyExistsException if a value has the 490: * same calculated index 491: * as an existing value or 492: * of one of the other 493: * specified values. 494: */ 495: public void putAll(CompositeData[] vals) 496: { 497: if (vals == null || vals.length == 0) 498: return; 499: Map mapToAdd = new HashMap(vals.length); 500: for (int a = 0; a < vals.length; ++a) 501: { 502: Object[] key = calculateIndex(vals[a]); 503: if (dataMap.containsKey(key)) 504: throw new KeyAlreadyExistsException("Element " + a + ": A " + 505: "value with this index " + 506: "already exists."); 507: mapToAdd.put(key, vals[a]); 508: } 509: dataMap.putAll(mapToAdd); 510: } 511: 512: /** 513: * Converts each value from the specified map to a member of an 514: * array of {@link CompositeData} values and adds them using {@link 515: * #put(CompositeData[])}, if possible. As in {@link 516: * #put(Object,Object)}, the keys are simply ignored. This method 517: * is useful for adding the {@link CompositeData} values from a 518: * different {@link TabularData} instance, which uses the same 519: * {@link TabularType} but a different selection of index names, to 520: * this one. If the map is <code>null</code> or empty, the method 521: * simply returns. 522: * 523: * @param m the map to add. Only the values are used and must 524: * all be instances of {@link CompositeData}. 525: * @throws NullPointerException if a value from the map is 526: * <code>null</code>. 527: * @throws ClassCastException if a value from the map is not 528: * an instance of {@link CompositeData}. 529: * @throws InvalidOpenTypeException if the type of the 530: * given value does not 531: * match the row type. 532: * @throws KeyAlreadyExistsException if the value has the 533: * same calculated index 534: * as an existing value or 535: * of one of the other 536: * specified values. 537: */ 538: public void putAll(Map<?,?> m) 539: { 540: if (m == null || m.size() == 0) 541: return; 542: Collection vals = m.values(); 543: CompositeData[] data = new CompositeData[vals.size()]; 544: Iterator it = vals.iterator(); 545: for (int a = 0; it.hasNext(); ++a) 546: { 547: data[a] = (CompositeData) it.next(); 548: } 549: putAll(data); 550: } 551: 552: /** 553: * Removes the value for the specified key by simply 554: * calling <code>remove((Object[]) key)</code>. 555: * 556: * @param key the key whose value should be removed. 557: * @return the removed value, or <code>null</code> if 558: * there is no value for the given key. 559: * @throws NullPointerException if the key is <code>null</code>. 560: * @throws ClassCastException if the key is not an instance 561: * of <code>Object[]</code>. 562: * @throws InvalidOpenTypeException if the key does not match 563: * the {@link TabularType} of this 564: * instance. 565: */ 566: public Object remove(Object key) 567: { 568: return remove((Object[]) key); 569: } 570: 571: /** 572: * Removes the {@link CompositeData} value located at the 573: * specified index. <code>null</code> is returned if the 574: * value does not exist. Otherwise, the removed value is 575: * returned. 576: * 577: * @param key the key of the value to remove. 578: * @return the removed value, or <code>null</code> if 579: * there is no value for the given key. 580: * @throws NullPointerException if the key is <code>null</code>. 581: * @throws InvalidOpenTypeException if the key does not match 582: * the {@link TabularType} of this 583: * instance. 584: */ 585: public CompositeData remove(Object[] key) 586: { 587: if (!(isKeyValid(key))) 588: throw new InvalidKeyException("The key does not match the " + 589: "tabular type of this instance."); 590: return (CompositeData) dataMap.remove(key); 591: } 592: 593: /** 594: * Package-private method to set the internal {@link java.util.Map} 595: * instance (used in cloning). 596: * 597: * @param map the new map used. 598: */ 599: void setMap(Map map) 600: { 601: dataMap = map; 602: } 603: 604: /** 605: * Returns the number of {@link CompositeData} values or rows 606: * in the table. 607: * 608: * @return the number of rows in the table. 609: */ 610: public int size() 611: { 612: return dataMap.size(); 613: } 614: 615: /** 616: * Returns a textual representation of this instance. This 617: * is constructed using the class name 618: * (<code>javax.management.openmbean.TabularDataSupport</code>) 619: * and the result of calling <code>toString()</code> on the 620: * tabular type and underlying hash map instance. 621: * 622: * @return a {@link java.lang.String} representation of the 623: * object. 624: */ 625: public String toString() 626: { 627: return getClass().getName() 628: + "[tabularType=" + tabularType 629: + ",dataMap=" + dataMap 630: + "]"; 631: } 632: 633: /** 634: * Returns a collection (or bag) view of the values in this Map. The 635: * collection is backed by the map, so that changes in one show up in 636: * the other. Modifications made while an iterator is in progress cause 637: * undefined behavior. If the collection supports removal, these methods 638: * remove the underlying mapping from the map: <code>Iterator.remove</code>, 639: * <code>Collection.remove</code>, <code>removeAll</code>, 640: * <code>retainAll</code>, and <code>clear</code>. Element addition, via 641: * <code>add</code> or <code>addAll</code>, is not supported via this 642: * collection. 643: * 644: * @return the collection view of all values 645: */ 646: public Collection<Object> values() 647: { 648: return dataMap.values(); 649: } 650: 651: }
GNU Classpath (0.95) |