1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * ident "%Z%%M% %I% %E% SMI" 27 */ 28 package org.opensolaris.os.dtrace; 29 30 import java.io.*; 31 import java.util.*; 32 import java.beans.*; 33 import java.util.*; 34 35 /** 36 * Multi-element key to a value in an {@link Aggregation}. 37 * <p> 38 * Tuple equality is based on the length of each tuple and the equality 39 * of each corresponding element. The natural ordering of tuples is 40 * based on a lenient comparison designed not to throw exceptions when 41 * corresponding elements are not mutually comparable or the number of 42 * tuple elements differs. 43 * <p> 44 * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. 45 * 46 * @author Tom Erickson 47 */ 48 public final class Tuple implements Serializable, Comparable <Tuple>, 49 Iterable<ValueRecord> 50 { 51 static final long serialVersionUID = 5192674716869462720L; 52 53 /** 54 * The empty tuple has zero elements and may be used to obtain the 55 * singleton {@link AggregationRecord} of a non-keyed {@link 56 * Aggregation}, such as the one derived from the D statement 57 * <code>@a = count()</code>. (In D, an aggregation without 58 * square brackets aggregates a single value.) 59 */ 60 public static final Tuple EMPTY = new Tuple(); 61 62 static { 63 try { 64 BeanInfo info = Introspector.getBeanInfo(Tuple.class); 65 PersistenceDelegate persistenceDelegate = 66 new DefaultPersistenceDelegate( 67 new String[] {"elements"}) 68 { 69 /* 70 * Need to prevent DefaultPersistenceDelegate from using 71 * overridden equals() method, resulting in a 72 * StackOverFlowError. Revert to PersistenceDelegate 73 * implementation. See 74 * http://forum.java.sun.com/thread.jspa?threadID= 75 * 477019&tstart=135 76 */ 77 protected boolean 78 mutatesTo(Object oldInstance, Object newInstance) 79 { 80 return (newInstance != null && oldInstance != null && 81 oldInstance.getClass() == newInstance.getClass()); 82 } 83 }; 84 BeanDescriptor d = info.getBeanDescriptor(); 85 d.setValue("persistenceDelegate", persistenceDelegate); 86 } catch (IntrospectionException e) { 87 System.out.println(e); 88 } 89 } 90 91 /** @serial */ 92 private java.util.List <ValueRecord> elements; 93 94 private Tuple()95 Tuple() 96 { 97 // 98 // expected to be a short list (usually one to three elements) 99 // 100 elements = new ArrayList <ValueRecord> (4); 101 } 102 103 /** 104 * Creates a tuple with the given elements in the given order. 105 * 106 * @param tupleElements ordered series of tuple elements 107 * @throws NullPointerException if the given array or any of its 108 * elements is {@code null} 109 */ 110 public Tuple(ValueRecord .... tupleElements)111 Tuple(ValueRecord ... tupleElements) 112 { 113 this(); 114 if (tupleElements == null) { 115 throw new NullPointerException("null array"); 116 } 117 for (ValueRecord r : tupleElements) { 118 if (r == null) { 119 throw new NullPointerException("null element"); 120 } 121 elements.add(r); 122 } 123 } 124 125 /** 126 * Creates a tuple with the given element list in the given list 127 * order. 128 * 129 * @param tupleElements ordered list of tuple elements 130 * @throws NullPointerException if the given list or any of its 131 * elements is {@code null} 132 */ 133 public Tuple(List <ValueRecord> tupleElements)134 Tuple(List <ValueRecord> tupleElements) 135 { 136 this(); 137 if (tupleElements == null) { 138 throw new NullPointerException("element list is null"); 139 } 140 for (ValueRecord r : tupleElements) { 141 if (r == null) { 142 throw new NullPointerException("null element"); 143 } 144 elements.add(r); 145 } 146 } 147 148 /** 149 * Called by native code. 150 * 151 * @throws NullPointerException if element is null 152 * @throws IllegalArgumentException if element is neither a {@link 153 * ValueRecord} nor one of the DTrace primitive types returned by 154 * {@link ScalarRecord#getValue()} 155 */ 156 private void addElement(ValueRecord element)157 addElement(ValueRecord element) 158 { 159 if (element == null) { 160 throw new NullPointerException("tuple element is null at " + 161 "index " + elements.size()); 162 } 163 elements.add(element); 164 } 165 166 /** 167 * Gets a modifiable list of this tuple's elements in the same order 168 * as their corresponding variables in the original D program tuple. 169 * Modifying the returned list has no effect on this tuple. 170 * Supports XML persistence. 171 * 172 * @return a modifiable list of this tuple's elements in the same order 173 * as their corresponding variables in the original D program tuple 174 */ 175 public List <ValueRecord> getElements()176 getElements() 177 { 178 return new ArrayList <ValueRecord> (elements); 179 } 180 181 /** 182 * Gets a read-only {@code List} view of this tuple. 183 * 184 * @return a read-only {@code List} view of this tuple 185 */ 186 public List <ValueRecord> asList()187 asList() 188 { 189 return Collections. <ValueRecord> unmodifiableList(elements); 190 } 191 192 /** 193 * Gets the number of elements in this tuple. 194 * 195 * @return non-negative element count 196 */ 197 public int size()198 size() 199 { 200 return elements.size(); 201 } 202 203 /** 204 * Returns {@code true} if this tuple has no elements. 205 * 206 * @return {@code true} if this tuple has no elements, {@code false} 207 * otherwise 208 * @see Tuple#EMPTY 209 */ 210 public boolean isEmpty()211 isEmpty() 212 { 213 return elements.isEmpty(); 214 } 215 216 /** 217 * Gets the element at the given tuple index (starting at zero). 218 * 219 * @return non-null tuple element at the given zero-based index 220 */ 221 public ValueRecord get(int index)222 get(int index) 223 { 224 return elements.get(index); 225 } 226 227 /** 228 * Gets an iterator over the elements of this tuple. 229 * 230 * @return an iterator over the elements of this tuple 231 */ 232 public Iterator<ValueRecord> iterator()233 iterator() 234 { 235 return elements.iterator(); 236 } 237 238 /** 239 * Compares the specified object with this {@code Tuple} instance 240 * for equality. Defines equality as having the same elements in 241 * the same order. 242 * 243 * @return {@code true} if and only if the specified object is of 244 * type {@code Tuple} and both instances have the same size and 245 * equal elements at corresponding tuple indexes 246 */ 247 public boolean equals(Object o)248 equals(Object o) 249 { 250 if (o instanceof Tuple) { 251 Tuple t = (Tuple)o; 252 return elements.equals(t.elements); 253 } 254 return false; 255 } 256 257 /** 258 * Overridden to ensure that equals instances have equal hash codes. 259 */ 260 public int hashCode()261 hashCode() 262 { 263 return elements.hashCode(); 264 } 265 266 // lenient sort does not throw exceptions 267 @SuppressWarnings("unchecked") 268 private static int compareObjects(Object o1, Object o2)269 compareObjects(Object o1, Object o2) 270 { 271 int cmp; 272 273 if (o1 instanceof Comparable) { 274 Class c1 = o1.getClass(); 275 Class c2 = o2.getClass(); 276 if (c1.equals(c2)) { 277 cmp = ProbeData.compareUnsigned(Comparable.class.cast(o1), 278 Comparable.class.cast(o2)); 279 } else { 280 // Compare string values. 281 String s1 = o1.toString(); 282 String s2 = o2.toString(); 283 cmp = s1.compareTo(s2); 284 } 285 } else if (o1 instanceof byte[] && o2 instanceof byte[]) { 286 byte[] a1 = byte[].class.cast(o1); 287 byte[] a2 = byte[].class.cast(o2); 288 cmp = ProbeData.compareByteArrays(a1, a2); 289 } else { 290 // Compare string values. 291 String s1 = o1.toString(); 292 String s2 = o2.toString(); 293 cmp = s1.compareTo(s2); 294 } 295 296 return cmp; 297 } 298 299 /** 300 * Defines the natural ordering of tuples. Uses a lenient algorithm 301 * designed not to throw exceptions. Sorts tuples by the natural 302 * ordering of corresponding elements, starting with the first pair 303 * of corresponding elements and comparing subsequent pairs only 304 * when all previous pairs are equal (as a tie breaker). If 305 * corresponding elements are not mutually comparable, it compares 306 * the string values of those elements. If all corresponding 307 * elements are equal, then the tuple with more elements sorts 308 * higher than the tuple with fewer elements. 309 * 310 * @return a negative integer, zero, or a postive integer as this 311 * tuple is less than, equal to, or greater than the given tuple 312 * @see Tuple#compare(Tuple t1, Tuple t2, int pos) 313 */ 314 public int compareTo(Tuple t)315 compareTo(Tuple t) 316 { 317 int cmp = 0; 318 int len = size(); 319 int tlen = t.size(); 320 for (int i = 0; (cmp == 0) && (i < len) && (i < tlen); ++i) { 321 cmp = Tuple.compare(this, t, i); 322 } 323 if (cmp == 0) { 324 cmp = (len < tlen ? -1 : (len > tlen ? 1 : 0)); 325 } 326 return cmp; 327 } 328 329 /** 330 * Compares corresponding tuple elements at the given zero-based 331 * index. Elements are ordered as defined in the native DTrace 332 * library, which treats integer values as unsigned when sorting. 333 * 334 * @param t1 first tuple 335 * @param t2 second tuple 336 * @param pos nth tuple element, starting at zero 337 * @return a negative integer, zero, or a postive integer as the 338 * element in the first tuple is less than, equal to, or greater 339 * than the element in the second tuple 340 * @throws IndexOutOfBoundsException if the given tuple index {@code 341 * pos} is out of range {@code (pos < 0 || pos >= size())} for 342 * either of the given tuples 343 */ 344 public static int compare(Tuple t1, Tuple t2, int pos)345 compare(Tuple t1, Tuple t2, int pos) 346 { 347 int cmp = 0; 348 ValueRecord rec1 = t1.get(pos); 349 ValueRecord rec2 = t2.get(pos); 350 Object val1; 351 Object val2; 352 if (rec1 instanceof ScalarRecord) { 353 val1 = rec1.getValue(); 354 } else { 355 val1 = rec1; 356 } 357 if (rec2 instanceof ScalarRecord) { 358 val2 = rec2.getValue(); 359 } else { 360 val2 = rec2; 361 } 362 cmp = compareObjects(val1, val2); 363 return (cmp); 364 } 365 366 private void readObject(ObjectInputStream s)367 readObject(ObjectInputStream s) 368 throws IOException, ClassNotFoundException 369 { 370 s.defaultReadObject(); 371 // Make a defensive copy of elements 372 if (elements == null) { 373 throw new InvalidObjectException("element list is null"); 374 } 375 List <ValueRecord> copy = new ArrayList <ValueRecord> 376 (elements.size()); 377 copy.addAll(elements); 378 elements = copy; 379 // check class invariants 380 for (ValueRecord e : elements) { 381 if (e == null) { 382 throw new InvalidObjectException("null element"); 383 } 384 } 385 } 386 387 /** 388 * Gets a string representation of this tuple's elements in the same 389 * format as that returned by {@link AbstractCollection#toString()}. 390 * The representation, although specified, is subject to change. 391 */ 392 public String toString()393 toString() 394 { 395 return elements.toString(); 396 } 397 } 398