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>&#64;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