/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * ident "%Z%%M% %I% %E% SMI" */ package org.opensolaris.os.dtrace; import java.io.*; import java.util.*; import java.beans.*; import java.util.*; /** * Multi-element key to a value in an {@link Aggregation}. *

* Tuple equality is based on the length of each tuple and the equality * of each corresponding element. The natural ordering of tuples is * based on a lenient comparison designed not to throw exceptions when * corresponding elements are not mutually comparable or the number of * tuple elements differs. *

* Immutable. Supports persistence using {@link java.beans.XMLEncoder}. * * @author Tom Erickson */ public final class Tuple implements Serializable, Comparable , Iterable { static final long serialVersionUID = 5192674716869462720L; /** * The empty tuple has zero elements and may be used to obtain the * singleton {@link AggregationRecord} of a non-keyed {@link * Aggregation}, such as the one derived from the D statement * @a = count(). (In D, an aggregation without * square brackets aggregates a single value.) */ public static final Tuple EMPTY = new Tuple(); static { try { BeanInfo info = Introspector.getBeanInfo(Tuple.class); PersistenceDelegate persistenceDelegate = new DefaultPersistenceDelegate( new String[] {"elements"}) { /* * Need to prevent DefaultPersistenceDelegate from using * overridden equals() method, resulting in a * StackOverFlowError. Revert to PersistenceDelegate * implementation. See * http://forum.java.sun.com/thread.jspa?threadID= * 477019&tstart=135 */ protected boolean mutatesTo(Object oldInstance, Object newInstance) { return (newInstance != null && oldInstance != null && oldInstance.getClass() == newInstance.getClass()); } }; BeanDescriptor d = info.getBeanDescriptor(); d.setValue("persistenceDelegate", persistenceDelegate); } catch (IntrospectionException e) { System.out.println(e); } } /** @serial */ private java.util.List elements; private Tuple() { // // expected to be a short list (usually one to three elements) // elements = new ArrayList (4); } /** * Creates a tuple with the given elements in the given order. * * @param tupleElements ordered series of tuple elements * @throws NullPointerException if the given array or any of its * elements is {@code null} */ public Tuple(ValueRecord ... tupleElements) { this(); if (tupleElements == null) { throw new NullPointerException("null array"); } for (ValueRecord r : tupleElements) { if (r == null) { throw new NullPointerException("null element"); } elements.add(r); } } /** * Creates a tuple with the given element list in the given list * order. * * @param tupleElements ordered list of tuple elements * @throws NullPointerException if the given list or any of its * elements is {@code null} */ public Tuple(List tupleElements) { this(); if (tupleElements == null) { throw new NullPointerException("element list is null"); } for (ValueRecord r : tupleElements) { if (r == null) { throw new NullPointerException("null element"); } elements.add(r); } } /** * Called by native code. * * @throws NullPointerException if element is null * @throws IllegalArgumentException if element is neither a {@link * ValueRecord} nor one of the DTrace primitive types returned by * {@link ScalarRecord#getValue()} */ private void addElement(ValueRecord element) { if (element == null) { throw new NullPointerException("tuple element is null at " + "index " + elements.size()); } elements.add(element); } /** * Gets a modifiable list of this tuple's elements in the same order * as their corresponding variables in the original D program tuple. * Modifying the returned list has no effect on this tuple. * Supports XML persistence. * * @return a modifiable list of this tuple's elements in the same order * as their corresponding variables in the original D program tuple */ public List getElements() { return new ArrayList (elements); } /** * Gets a read-only {@code List} view of this tuple. * * @return a read-only {@code List} view of this tuple */ public List asList() { return Collections. unmodifiableList(elements); } /** * Gets the number of elements in this tuple. * * @return non-negative element count */ public int size() { return elements.size(); } /** * Returns {@code true} if this tuple has no elements. * * @return {@code true} if this tuple has no elements, {@code false} * otherwise * @see Tuple#EMPTY */ public boolean isEmpty() { return elements.isEmpty(); } /** * Gets the element at the given tuple index (starting at zero). * * @return non-null tuple element at the given zero-based index */ public ValueRecord get(int index) { return elements.get(index); } /** * Gets an iterator over the elements of this tuple. * * @return an iterator over the elements of this tuple */ public Iterator iterator() { return elements.iterator(); } /** * Compares the specified object with this {@code Tuple} instance * for equality. Defines equality as having the same elements in * the same order. * * @return {@code true} if and only if the specified object is of * type {@code Tuple} and both instances have the same size and * equal elements at corresponding tuple indexes */ public boolean equals(Object o) { if (o instanceof Tuple) { Tuple t = (Tuple)o; return elements.equals(t.elements); } return false; } /** * Overridden to ensure that equals instances have equal hash codes. */ public int hashCode() { return elements.hashCode(); } // lenient sort does not throw exceptions @SuppressWarnings("unchecked") private static int compareObjects(Object o1, Object o2) { int cmp; if (o1 instanceof Comparable) { Class c1 = o1.getClass(); Class c2 = o2.getClass(); if (c1.equals(c2)) { cmp = ProbeData.compareUnsigned(Comparable.class.cast(o1), Comparable.class.cast(o2)); } else { // Compare string values. String s1 = o1.toString(); String s2 = o2.toString(); cmp = s1.compareTo(s2); } } else if (o1 instanceof byte[] && o2 instanceof byte[]) { byte[] a1 = byte[].class.cast(o1); byte[] a2 = byte[].class.cast(o2); cmp = ProbeData.compareByteArrays(a1, a2); } else { // Compare string values. String s1 = o1.toString(); String s2 = o2.toString(); cmp = s1.compareTo(s2); } return cmp; } /** * Defines the natural ordering of tuples. Uses a lenient algorithm * designed not to throw exceptions. Sorts tuples by the natural * ordering of corresponding elements, starting with the first pair * of corresponding elements and comparing subsequent pairs only * when all previous pairs are equal (as a tie breaker). If * corresponding elements are not mutually comparable, it compares * the string values of those elements. If all corresponding * elements are equal, then the tuple with more elements sorts * higher than the tuple with fewer elements. * * @return a negative integer, zero, or a postive integer as this * tuple is less than, equal to, or greater than the given tuple * @see Tuple#compare(Tuple t1, Tuple t2, int pos) */ public int compareTo(Tuple t) { int cmp = 0; int len = size(); int tlen = t.size(); for (int i = 0; (cmp == 0) && (i < len) && (i < tlen); ++i) { cmp = Tuple.compare(this, t, i); } if (cmp == 0) { cmp = (len < tlen ? -1 : (len > tlen ? 1 : 0)); } return cmp; } /** * Compares corresponding tuple elements at the given zero-based * index. Elements are ordered as defined in the native DTrace * library, which treats integer values as unsigned when sorting. * * @param t1 first tuple * @param t2 second tuple * @param pos nth tuple element, starting at zero * @return a negative integer, zero, or a postive integer as the * element in the first tuple is less than, equal to, or greater * than the element in the second tuple * @throws IndexOutOfBoundsException if the given tuple index {@code * pos} is out of range {@code (pos < 0 || pos >= size())} for * either of the given tuples */ public static int compare(Tuple t1, Tuple t2, int pos) { int cmp = 0; ValueRecord rec1 = t1.get(pos); ValueRecord rec2 = t2.get(pos); Object val1; Object val2; if (rec1 instanceof ScalarRecord) { val1 = rec1.getValue(); } else { val1 = rec1; } if (rec2 instanceof ScalarRecord) { val2 = rec2.getValue(); } else { val2 = rec2; } cmp = compareObjects(val1, val2); return (cmp); } private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); // Make a defensive copy of elements if (elements == null) { throw new InvalidObjectException("element list is null"); } List copy = new ArrayList (elements.size()); copy.addAll(elements); elements = copy; // check class invariants for (ValueRecord e : elements) { if (e == null) { throw new InvalidObjectException("null element"); } } } /** * Gets a string representation of this tuple's elements in the same * format as that returned by {@link AbstractCollection#toString()}. * The representation, although specified, is subject to change. */ public String toString() { return elements.toString(); } }