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 2006 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 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 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 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 157 addElement(Object element) 158 { 159 if (element == null) { 160 throw new NullPointerException("tuple element is null at " + 161 "index " + elements.size()); 162 } 163 if (element instanceof ValueRecord) { 164 elements.add(ValueRecord.class.cast(element)); 165 } else { 166 elements.add(new ScalarRecord(element)); 167 } 168 } 169 170 /** 171 * Gets a modifiable list of this tuple's elements in the same order 172 * as their corresponding variables in the original D program tuple. 173 * Modifying the returned list has no effect on this tuple. 174 * Supports XML persistence. 175 * 176 * @return a modifiable list of this tuple's elements in the same order 177 * as their corresponding variables in the original D program tuple 178 */ 179 public List <ValueRecord> 180 getElements() 181 { 182 return new ArrayList <ValueRecord> (elements); 183 } 184 185 /** 186 * Gets a read-only {@code List} view of this tuple. 187 * 188 * @return a read-only {@code List} view of this tuple 189 */ 190 public List <ValueRecord> 191 asList() 192 { 193 return Collections.unmodifiableList(elements); 194 } 195 196 /** 197 * Gets the number of elements in this tuple. 198 * 199 * @return non-negative element count 200 */ 201 public int 202 size() 203 { 204 return elements.size(); 205 } 206 207 /** 208 * Returns {@code true} if this tuple has no elements. 209 * 210 * @return {@code true} if this tuple has no elements, {@code false} 211 * otherwise 212 * @see Tuple#EMPTY 213 */ 214 public boolean 215 isEmpty() 216 { 217 return elements.isEmpty(); 218 } 219 220 /** 221 * Gets the element at the given tuple index (starting at zero). 222 * 223 * @return non-null tuple element at the given zero-based index 224 */ 225 public ValueRecord 226 get(int index) 227 { 228 return elements.get(index); 229 } 230 231 /** 232 * Gets an iterator over the elements of this tuple. 233 * 234 * @return an iterator over the elements of this tuple 235 */ 236 public Iterator<ValueRecord> 237 iterator() 238 { 239 return elements.iterator(); 240 } 241 242 /** 243 * Compares the specified object with this {@code Tuple} instance 244 * for equality. Defines equality as having the same elements in 245 * the same order. 246 * 247 * @return {@code true} if and only if the specified object is of 248 * type {@code Tuple} and both instances have the same size and 249 * equal elements at corresponding tuple indexes 250 */ 251 public boolean 252 equals(Object o) 253 { 254 if (o instanceof Tuple) { 255 Tuple t = (Tuple)o; 256 return elements.equals(t.elements); 257 } 258 return false; 259 } 260 261 /** 262 * Overridden to ensure that equals instances have equal hash codes. 263 */ 264 public int 265 hashCode() 266 { 267 return elements.hashCode(); 268 } 269 270 // lenient sort does not throw exceptions 271 @SuppressWarnings("unchecked") 272 private int 273 compareObjects(Object o1, Object o2) 274 { 275 int cmp; 276 Class c1 = o1.getClass(); 277 Class c2 = o2.getClass(); 278 if (c1.isAssignableFrom(c2) && (o1 instanceof Comparable)) { 279 Comparable c = Comparable.class.cast(o1); 280 cmp = c.compareTo(c1.cast(o2)); 281 } else { 282 // Compare string values. If matching, compare object class. 283 String s1 = o1.toString(); 284 String s2 = o2.toString(); 285 cmp = s1.compareTo(s2); 286 if (cmp == 0) { 287 cmp = c1.getName().compareTo(c2.getName()); 288 } 289 } 290 return cmp; 291 } 292 293 /** 294 * Defines the natural ordering of tuples. Uses a lenient algorithm 295 * designed not to throw exceptions. Sorts tuples by the natural 296 * ordering of corresponding elements, starting with the first pair 297 * of corresponding elements and comparing subsequent pairs only 298 * when all previous pairs are equal (as a tie breaker). If 299 * corresponding elements are not mutually comparable, it compares 300 * the string values of those elements, then if the string values 301 * are equal, it compares the class names of those elements' types. 302 * If all corresponding elements are equal, then the tuple with more 303 * elements sorts higher than the tuple with fewer elements. 304 * 305 * @return a negative integer, zero, or a postive integer as this 306 * tuple is less than, equal to, or greater than the given tuple 307 */ 308 public int 309 compareTo(Tuple t) 310 { 311 int cmp = 0; 312 int len = size(); 313 int tlen = t.size(); 314 ValueRecord rec; 315 ValueRecord trec; 316 Object val; 317 Object tval; 318 for (int i = 0; (cmp == 0) && (i < len) && (i < tlen); ++i) { 319 rec = get(i); 320 trec = t.get(i); 321 if (rec instanceof ScalarRecord) { 322 val = rec.getValue(); 323 } else { 324 val = rec; 325 } 326 if (trec instanceof ScalarRecord) { 327 tval = trec.getValue(); 328 } else { 329 tval = trec; 330 } 331 cmp = compareObjects(val, tval); 332 } 333 if (cmp == 0) { 334 cmp = (len < tlen ? -1 : (len > tlen ? 1 : 0)); 335 } 336 return cmp; 337 } 338 339 private void 340 readObject(ObjectInputStream s) 341 throws IOException, ClassNotFoundException 342 { 343 s.defaultReadObject(); 344 // Make a defensive copy of elements 345 if (elements == null) { 346 throw new InvalidObjectException("element list is null"); 347 } 348 List <ValueRecord> copy = new ArrayList <ValueRecord> 349 (elements.size()); 350 copy.addAll(elements); 351 elements = copy; 352 // check class invariants 353 for (ValueRecord e : elements) { 354 if (e == null) { 355 throw new InvalidObjectException("null element"); 356 } 357 } 358 } 359 360 /** 361 * Gets a string representation of this tuple's elements in the same 362 * format as that returned by {@link AbstractCollection#toString()}. 363 * The representation, although specified, is subject to change. 364 */ 365 public String 366 toString() 367 { 368 return elements.toString(); 369 } 370 } 371