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