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.util.*; 31fb3fb4f3Stomee import java.io.*; 32fb3fb4f3Stomee import java.beans.*; 33fb3fb4f3Stomee 34fb3fb4f3Stomee /** 35fb3fb4f3Stomee * A frequency distribution aggregated by the DTrace {@code quantize()} 36fb3fb4f3Stomee * or {@code lquantize()} action. Each aggregated value falls into a 37fb3fb4f3Stomee * range known as a bucket and counts toward the frequency of that 38fb3fb4f3Stomee * bucket. Bucket ranges are consecutive, with the maximum of one 39fb3fb4f3Stomee * bucket's range always one less than the minimum of the next bucket's 40fb3fb4f3Stomee * range. By convention each bucket is identified by the minimum of its 41fb3fb4f3Stomee * range. 42fb3fb4f3Stomee * 43fb3fb4f3Stomee * @author Tom Erickson 44fb3fb4f3Stomee */ 45fb3fb4f3Stomee public abstract class Distribution implements AggregationValue, 46fb3fb4f3Stomee Iterable <Distribution.Bucket>, Serializable 47fb3fb4f3Stomee { 48fb3fb4f3Stomee static final long serialVersionUID = 1186243118882654932L; 49fb3fb4f3Stomee 50fb3fb4f3Stomee /** @serial */ 51fb3fb4f3Stomee private List <Bucket> buckets; 52fb3fb4f3Stomee private transient double total; 53fb3fb4f3Stomee private transient boolean initialized; 54fb3fb4f3Stomee 55fb3fb4f3Stomee /** 56fb3fb4f3Stomee * Package level access, called by subclasses LinearDistribution and 57fb3fb4f3Stomee * LogDistribution, but not available outside the API. 58fb3fb4f3Stomee * 59fb3fb4f3Stomee * @param base the lower bound of this distribution, or zero if not 60fb3fb4f3Stomee * applicable 61fb3fb4f3Stomee * @param constant the constant term of the distribution function 62fb3fb4f3Stomee * used to calculate the lower bound of any bucket given the lower 63fb3fb4f3Stomee * bound of the previous bucket, for example the step in a linear 64fb3fb4f3Stomee * distribution or the log base in a logarithmic distribution. 65fb3fb4f3Stomee * @param frequencies for each bucket, the number of aggregated 66fb3fb4f3Stomee * values falling into that bucket's range; each element must be a 67fb3fb4f3Stomee * positive integer 68fb3fb4f3Stomee * @throws NullPointerException if frequencies is null 69fb3fb4f3Stomee * @throws IllegalArgumentException if any element of frequencies 70fb3fb4f3Stomee * does not have the expected range as defined by checkBucketRange() 71fb3fb4f3Stomee */ Distribution(long base, long constant, long[] frequencies)72fb3fb4f3Stomee Distribution(long base, long constant, long[] frequencies) 73fb3fb4f3Stomee { 74fb3fb4f3Stomee total = 0; 75fb3fb4f3Stomee long frequency; 76fb3fb4f3Stomee for (int i = 0, len = frequencies.length; i < len; ++i) { 77fb3fb4f3Stomee frequency = frequencies[i]; 78fb3fb4f3Stomee total += frequency; 79fb3fb4f3Stomee } 80fb3fb4f3Stomee 81fb3fb4f3Stomee buckets = createBuckets(base, constant, frequencies); 82fb3fb4f3Stomee initialized = true; 83fb3fb4f3Stomee } 84fb3fb4f3Stomee 85fb3fb4f3Stomee /** 86fb3fb4f3Stomee * Supports XML persistence of subclasses. Sub-class implementation 87fb3fb4f3Stomee * must call initialize() after setting any state specific to that 88fb3fb4f3Stomee * subclass for determining bucket ranges. 89fb3fb4f3Stomee * 90fb3fb4f3Stomee * @throws NullPointerException if frequencies is null 91fb3fb4f3Stomee * @throws IllegalArgumentException if any element of frequencies 92fb3fb4f3Stomee * does not have the expected range as defined by checkBucketRange() 93fb3fb4f3Stomee */ Distribution(List <Bucket> frequencies)94fb3fb4f3Stomee Distribution(List <Bucket> frequencies) 95fb3fb4f3Stomee { 96fb3fb4f3Stomee // defensively copy frequencies list 97fb3fb4f3Stomee int len = frequencies.size(); 98fb3fb4f3Stomee // don't need gratuitous capacity % added by constructor that 99fb3fb4f3Stomee // takes a Collection argument; list will not be modified 100fb3fb4f3Stomee buckets = new ArrayList <Bucket> (len); 101fb3fb4f3Stomee buckets.addAll(frequencies); 102fb3fb4f3Stomee } 103fb3fb4f3Stomee 104fb3fb4f3Stomee final void initialize()105fb3fb4f3Stomee initialize() 106fb3fb4f3Stomee { 107fb3fb4f3Stomee // Called by constructor and readObject() (deserialization). 108fb3fb4f3Stomee // 1. Check class invariants, throw exception if deserialized 109fb3fb4f3Stomee // state inconsistent with a Distribution that can result 110fb3fb4f3Stomee // from the public constructor. 111fb3fb4f3Stomee // 2. Compute total (transient property derived from buckets) 112fb3fb4f3Stomee total = 0; 113fb3fb4f3Stomee long frequency; 114fb3fb4f3Stomee Bucket bucket; 115fb3fb4f3Stomee int len = buckets.size(); 116fb3fb4f3Stomee for (int i = 0; i < len; ++i) { 117fb3fb4f3Stomee bucket = buckets.get(i); 118fb3fb4f3Stomee frequency = bucket.getFrequency(); 119fb3fb4f3Stomee // relies on package-private getBucketRange() 120fb3fb4f3Stomee // implementation 121fb3fb4f3Stomee checkBucketRange(i, len, bucket); 122fb3fb4f3Stomee total += frequency; 123fb3fb4f3Stomee } 124fb3fb4f3Stomee initialized = true; 125fb3fb4f3Stomee } 126fb3fb4f3Stomee 127fb3fb4f3Stomee // Must be called by public instance methods (since the AbstractList 128fb3fb4f3Stomee // methods all depend on get() and size(), it is sufficient to call 129fb3fb4f3Stomee // checkInit() only in those inherited methods). 130fb3fb4f3Stomee private void checkInit()131fb3fb4f3Stomee checkInit() 132fb3fb4f3Stomee { 133fb3fb4f3Stomee if (!initialized) { 134fb3fb4f3Stomee throw new IllegalStateException("Uninitialized"); 135fb3fb4f3Stomee } 136fb3fb4f3Stomee } 137fb3fb4f3Stomee 138fb3fb4f3Stomee /** 139fb3fb4f3Stomee * Gets a two element array: the first elelemt is the range minimum 140fb3fb4f3Stomee * (inclusive), the second element is the range maximum (inclusive). 141fb3fb4f3Stomee * Implemented by subclasses LinearDistribution and LogDistribution 142fb3fb4f3Stomee * to define bucket ranges for this distribution and not available 143fb3fb4f3Stomee * outside the API. Used by the private general purpose constructor 144fb3fb4f3Stomee * called from native code. Implementation must not use 145fb3fb4f3Stomee * subclass-specific state, since subclass state has not yet been 146fb3fb4f3Stomee * allocated. 147fb3fb4f3Stomee * 148fb3fb4f3Stomee * @see #Distribution(long base, long constant, long[] frequencies) 149fb3fb4f3Stomee */ 150fb3fb4f3Stomee abstract long[] getBucketRange(int i, int len, long base, long constant)151fb3fb4f3Stomee getBucketRange(int i, int len, long base, long constant); 152fb3fb4f3Stomee 153fb3fb4f3Stomee /** 154fb3fb4f3Stomee * Used by public constructors and deserialization only after 155fb3fb4f3Stomee * state specific to the subclass is available to the method. 156fb3fb4f3Stomee */ 157fb3fb4f3Stomee abstract long[] getBucketRange(int i, int len)158fb3fb4f3Stomee getBucketRange(int i, int len); 159fb3fb4f3Stomee 160fb3fb4f3Stomee private List <Distribution.Bucket> createBuckets(long base, long constant, long[] frequencies)161fb3fb4f3Stomee createBuckets(long base, long constant, long[] frequencies) 162fb3fb4f3Stomee { 163fb3fb4f3Stomee int len = frequencies.length; 164fb3fb4f3Stomee Bucket bucket; 165fb3fb4f3Stomee List <Bucket> buckets = new ArrayList <Bucket> (len); 166fb3fb4f3Stomee long min; // current bucket 167fb3fb4f3Stomee long max; // next bucket minus one 168fb3fb4f3Stomee long[] range; // two element array: { min, max } 169fb3fb4f3Stomee 170fb3fb4f3Stomee for (int i = 0; i < len; ++i) { 171fb3fb4f3Stomee range = getBucketRange(i, len, base, constant); 172fb3fb4f3Stomee min = range[0]; 173fb3fb4f3Stomee max = range[1]; 174fb3fb4f3Stomee bucket = new Distribution.Bucket(min, max, frequencies[i]); 175fb3fb4f3Stomee buckets.add(bucket); 176fb3fb4f3Stomee } 177fb3fb4f3Stomee 178fb3fb4f3Stomee return buckets; 179fb3fb4f3Stomee } 180fb3fb4f3Stomee 181fb3fb4f3Stomee /** 182fb3fb4f3Stomee * Validates that bucket has the expected range for the given bucket 183fb3fb4f3Stomee * index. Uses {@code base} and {@code constant} constructor args 184fb3fb4f3Stomee * to check invariants specific to each subclass, since state 185fb3fb4f3Stomee * derived from these args in a subclass is not yet available in the 186fb3fb4f3Stomee * superclass constructor. 187fb3fb4f3Stomee * 188fb3fb4f3Stomee * @throws IllegalArgumentException if bucket does not have the 189fb3fb4f3Stomee * expected range for the given bucket index {@code i} 190fb3fb4f3Stomee */ 191fb3fb4f3Stomee private void checkBucketRange(int i, int bucketCount, Distribution.Bucket bucket, long base, long constant)192fb3fb4f3Stomee checkBucketRange(int i, int bucketCount, Distribution.Bucket bucket, 193fb3fb4f3Stomee long base, long constant) 194fb3fb4f3Stomee { 195fb3fb4f3Stomee long[] range = getBucketRange(i, bucketCount, base, constant); 196fb3fb4f3Stomee checkBucketRange(i, bucket, range); 197fb3fb4f3Stomee } 198fb3fb4f3Stomee 199fb3fb4f3Stomee private void checkBucketRange(int i, int bucketCount, Distribution.Bucket bucket)200fb3fb4f3Stomee checkBucketRange(int i, int bucketCount, Distribution.Bucket bucket) 201fb3fb4f3Stomee { 202fb3fb4f3Stomee long[] range = getBucketRange(i, bucketCount); 203fb3fb4f3Stomee checkBucketRange(i, bucket, range); 204fb3fb4f3Stomee } 205fb3fb4f3Stomee 206fb3fb4f3Stomee private void checkBucketRange(int i, Distribution.Bucket bucket, long[] range)207fb3fb4f3Stomee checkBucketRange(int i, Distribution.Bucket bucket, long[] range) 208fb3fb4f3Stomee { 209fb3fb4f3Stomee long min = range[0]; 210fb3fb4f3Stomee long max = range[1]; 211fb3fb4f3Stomee 212fb3fb4f3Stomee if (bucket.getMin() != min) { 213fb3fb4f3Stomee throw new IllegalArgumentException("bucket min " + 214fb3fb4f3Stomee bucket.getMin() + " at index " + i + ", expected " + min); 215fb3fb4f3Stomee } 216fb3fb4f3Stomee if (bucket.getMax() != max) { 217fb3fb4f3Stomee throw new IllegalArgumentException("bucket max " + 218fb3fb4f3Stomee bucket.getMax() + " at index " + i + ", expected " + max); 219fb3fb4f3Stomee } 220fb3fb4f3Stomee } 221fb3fb4f3Stomee 222fb3fb4f3Stomee /** 223fb3fb4f3Stomee * Gets a modifiable list of this distribution's buckets ordered by 224fb3fb4f3Stomee * bucket range. Modifying the returned list has no effect on this 225fb3fb4f3Stomee * distribution. Supports XML persistence. 226fb3fb4f3Stomee * 227fb3fb4f3Stomee * @return a modifiable list of this distribution's buckets ordered 228fb3fb4f3Stomee * by bucket range 229fb3fb4f3Stomee */ 230fb3fb4f3Stomee public List <Bucket> getBuckets()231fb3fb4f3Stomee getBuckets() 232fb3fb4f3Stomee { 233fb3fb4f3Stomee checkInit(); 234fb3fb4f3Stomee return new ArrayList <Bucket> (buckets); 235fb3fb4f3Stomee } 236fb3fb4f3Stomee 237fb3fb4f3Stomee /** 238*e77b06d2Stomee * Gets a read-only {@code List} view of this distribution. 239fb3fb4f3Stomee * 240*e77b06d2Stomee * @return a read-only {@code List} view of this distribution 241fb3fb4f3Stomee */ 242fb3fb4f3Stomee public List <Bucket> asList()243fb3fb4f3Stomee asList() 244fb3fb4f3Stomee { 245fb3fb4f3Stomee checkInit(); 246*e77b06d2Stomee return Collections. <Bucket> unmodifiableList(buckets); 247fb3fb4f3Stomee } 248fb3fb4f3Stomee 249fb3fb4f3Stomee /** 250fb3fb4f3Stomee * Gets the number of buckets in this distribution. 251fb3fb4f3Stomee * 252fb3fb4f3Stomee * @return non-negative bucket count 253fb3fb4f3Stomee */ 254fb3fb4f3Stomee public int size()255fb3fb4f3Stomee size() 256fb3fb4f3Stomee { 257fb3fb4f3Stomee checkInit(); 258fb3fb4f3Stomee return buckets.size(); 259fb3fb4f3Stomee } 260fb3fb4f3Stomee 261fb3fb4f3Stomee /** 262fb3fb4f3Stomee * Gets the bucket at the given distribution index (starting at 263fb3fb4f3Stomee * zero). 264fb3fb4f3Stomee * 265fb3fb4f3Stomee * @return non-null distribution bucket at the given zero-based 266fb3fb4f3Stomee * index 267fb3fb4f3Stomee */ 268fb3fb4f3Stomee public Bucket get(int index)269fb3fb4f3Stomee get(int index) 270fb3fb4f3Stomee { 271fb3fb4f3Stomee checkInit(); 272fb3fb4f3Stomee return buckets.get(index); 273fb3fb4f3Stomee } 274fb3fb4f3Stomee 275fb3fb4f3Stomee /** 276fb3fb4f3Stomee * Gets an iterator over the buckets of this distribution. 277fb3fb4f3Stomee * 278fb3fb4f3Stomee * @return an iterator over the buckets of this distribution 279fb3fb4f3Stomee */ 280fb3fb4f3Stomee public Iterator<Bucket> iterator()281fb3fb4f3Stomee iterator() 282fb3fb4f3Stomee { 283fb3fb4f3Stomee checkInit(); 284fb3fb4f3Stomee return buckets.iterator(); 285fb3fb4f3Stomee } 286fb3fb4f3Stomee 287fb3fb4f3Stomee /** 288fb3fb4f3Stomee * Compares the specified object with this {@code Distribution} 289fb3fb4f3Stomee * instance for equality. Defines equality as having the same 290fb3fb4f3Stomee * buckets with the same values. 291fb3fb4f3Stomee * 292fb3fb4f3Stomee * @return {@code true} if and only if the specified object is of 293fb3fb4f3Stomee * type {@code Distribution} and both instances have the same size 294fb3fb4f3Stomee * and equal buckets at corresponding distribution indexes 295fb3fb4f3Stomee */ 296fb3fb4f3Stomee public boolean equals(Object o)297fb3fb4f3Stomee equals(Object o) 298fb3fb4f3Stomee { 299fb3fb4f3Stomee checkInit(); 300fb3fb4f3Stomee if (o instanceof Distribution) { 301fb3fb4f3Stomee Distribution d = (Distribution)o; 302fb3fb4f3Stomee return buckets.equals(d.buckets); 303fb3fb4f3Stomee } 304fb3fb4f3Stomee return false; 305fb3fb4f3Stomee } 306fb3fb4f3Stomee 307fb3fb4f3Stomee /** 308fb3fb4f3Stomee * Overridden to ensure that equals instances have equal hash codes. 309fb3fb4f3Stomee */ 310fb3fb4f3Stomee public int hashCode()311fb3fb4f3Stomee hashCode() 312fb3fb4f3Stomee { 313fb3fb4f3Stomee checkInit(); 314fb3fb4f3Stomee return buckets.hashCode(); 315fb3fb4f3Stomee } 316fb3fb4f3Stomee 317fb3fb4f3Stomee /** 318fb3fb4f3Stomee * Gets the total frequency across all buckets. 319fb3fb4f3Stomee * 320fb3fb4f3Stomee * @return sum of the frequency of all buckets in this distribution 321fb3fb4f3Stomee */ 322fb3fb4f3Stomee public double getTotal()323fb3fb4f3Stomee getTotal() 324fb3fb4f3Stomee { 325fb3fb4f3Stomee checkInit(); 326fb3fb4f3Stomee return total; 327fb3fb4f3Stomee } 328fb3fb4f3Stomee 329fb3fb4f3Stomee /** 330fb3fb4f3Stomee * Gets the numeric value of this distribution used to compare 331fb3fb4f3Stomee * distributions by overall magnitude, defined as the sum total of 332fb3fb4f3Stomee * each bucket's frequency times the minimum of its range. 333fb3fb4f3Stomee */ getValue()334fb3fb4f3Stomee public abstract Number getValue(); 335fb3fb4f3Stomee 336fb3fb4f3Stomee /** 337fb3fb4f3Stomee * Called by native code 338fb3fb4f3Stomee */ 339fb3fb4f3Stomee private void normalizeBuckets(long normal)340fb3fb4f3Stomee normalizeBuckets(long normal) 341fb3fb4f3Stomee { 342fb3fb4f3Stomee for (Bucket b : buckets) { 343fb3fb4f3Stomee b.frequency /= normal; 344fb3fb4f3Stomee } 345fb3fb4f3Stomee } 346fb3fb4f3Stomee 347fb3fb4f3Stomee /** 348fb3fb4f3Stomee * A range inclusive at both endpoints and a count of aggregated 349fb3fb4f3Stomee * values that fall in that range. Buckets in a {@link 350fb3fb4f3Stomee * Distribution} are consecutive, such that the max of one bucket is 351fb3fb4f3Stomee * always one less than the min of the next bucket (or {@link 352fb3fb4f3Stomee * Long#MAX_VALUE} if it is the last bucket in the {@code 353fb3fb4f3Stomee * Distribution}). 354fb3fb4f3Stomee * <p> 355fb3fb4f3Stomee * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. 356fb3fb4f3Stomee */ 357fb3fb4f3Stomee public static final class Bucket implements Serializable { 358fb3fb4f3Stomee static final long serialVersionUID = 4863264115375406295L; 359fb3fb4f3Stomee 360fb3fb4f3Stomee /** @serial */ 361fb3fb4f3Stomee private final long min; 362fb3fb4f3Stomee /** @serial */ 363fb3fb4f3Stomee private final long max; 364fb3fb4f3Stomee /** @serial */ 365fb3fb4f3Stomee private long frequency; // non-final so native code can normalize 366fb3fb4f3Stomee 367fb3fb4f3Stomee static { 368fb3fb4f3Stomee try { 369fb3fb4f3Stomee BeanInfo info = Introspector.getBeanInfo(Bucket.class); 370fb3fb4f3Stomee PersistenceDelegate persistenceDelegate = 371fb3fb4f3Stomee new DefaultPersistenceDelegate( 372fb3fb4f3Stomee new String[] {"min", "max", "frequency"}) 373fb3fb4f3Stomee { 374fb3fb4f3Stomee /* 375fb3fb4f3Stomee * Need to prevent DefaultPersistenceDelegate from using 376fb3fb4f3Stomee * overridden equals() method, resulting in a 377fb3fb4f3Stomee * StackOverFlowError. Revert to PersistenceDelegate 378fb3fb4f3Stomee * implementation. See 379fb3fb4f3Stomee * http://forum.java.sun.com/thread.jspa?threadID= 380fb3fb4f3Stomee * 477019&tstart=135 381fb3fb4f3Stomee */ 382fb3fb4f3Stomee protected boolean 383fb3fb4f3Stomee mutatesTo(Object oldInstance, Object newInstance) 384fb3fb4f3Stomee { 385fb3fb4f3Stomee return (newInstance != null && oldInstance != null && 386fb3fb4f3Stomee (oldInstance.getClass() == 387fb3fb4f3Stomee newInstance.getClass())); 388fb3fb4f3Stomee } 389fb3fb4f3Stomee }; 390fb3fb4f3Stomee BeanDescriptor d = info.getBeanDescriptor(); 391fb3fb4f3Stomee d.setValue("persistenceDelegate", persistenceDelegate); 392fb3fb4f3Stomee } catch (IntrospectionException e) { 393fb3fb4f3Stomee System.out.println(e); 394fb3fb4f3Stomee } 395fb3fb4f3Stomee } 396fb3fb4f3Stomee 397fb3fb4f3Stomee /** 398fb3fb4f3Stomee * Creates a distribution bucket with the given range and 399fb3fb4f3Stomee * frequency. 400fb3fb4f3Stomee * 401fb3fb4f3Stomee * @param rangeMinimumInclusive sets the lower bound (inclusive) 402fb3fb4f3Stomee * returned by {@link #getMin()} 403fb3fb4f3Stomee * @param rangeMaximumInclusive sets the upper bound (inclusive) 404fb3fb4f3Stomee * returned by {@link #getMax()} 405fb3fb4f3Stomee * @param valuesInRange sets the value frequency in this 406fb3fb4f3Stomee * bucket's range returned by {@link #getFrequency()} 407fb3fb4f3Stomee * @throws IllegalArgumentException if {@code 408fb3fb4f3Stomee * rangeMaximumInclusive} is less than {@code 409fb3fb4f3Stomee * rangeMinimumInclusive} 410fb3fb4f3Stomee */ 411fb3fb4f3Stomee public Bucket(long rangeMinimumInclusive, long rangeMaximumInclusive, long valuesInRange)412fb3fb4f3Stomee Bucket(long rangeMinimumInclusive, long rangeMaximumInclusive, 413fb3fb4f3Stomee long valuesInRange) 414fb3fb4f3Stomee { 415fb3fb4f3Stomee if (rangeMaximumInclusive < rangeMinimumInclusive) { 416fb3fb4f3Stomee throw new IllegalArgumentException("upper bound " + 417fb3fb4f3Stomee rangeMaximumInclusive + " is less than lower bound " + 418fb3fb4f3Stomee rangeMinimumInclusive); 419fb3fb4f3Stomee } 420fb3fb4f3Stomee 421fb3fb4f3Stomee min = rangeMinimumInclusive; 422fb3fb4f3Stomee max = rangeMaximumInclusive; 423fb3fb4f3Stomee frequency = valuesInRange; 424fb3fb4f3Stomee } 425fb3fb4f3Stomee 426fb3fb4f3Stomee /** 427fb3fb4f3Stomee * Gets the lower bound of this bucket's range (inclusive). 428fb3fb4f3Stomee */ 429fb3fb4f3Stomee public long getMin()430fb3fb4f3Stomee getMin() 431fb3fb4f3Stomee { 432fb3fb4f3Stomee return min; 433fb3fb4f3Stomee } 434fb3fb4f3Stomee 435fb3fb4f3Stomee /** 436fb3fb4f3Stomee * Gets the upper bound of this bucket's range (inclusive). 437fb3fb4f3Stomee */ 438fb3fb4f3Stomee public long getMax()439fb3fb4f3Stomee getMax() 440fb3fb4f3Stomee { 441fb3fb4f3Stomee return max; 442fb3fb4f3Stomee } 443fb3fb4f3Stomee 444fb3fb4f3Stomee /** 445fb3fb4f3Stomee * Gets the number of values in a {@link Distribution} that fall 446fb3fb4f3Stomee * into the range defined by this bucket. 447fb3fb4f3Stomee */ 448fb3fb4f3Stomee public long getFrequency()449fb3fb4f3Stomee getFrequency() 450fb3fb4f3Stomee { 451fb3fb4f3Stomee return frequency; 452fb3fb4f3Stomee } 453fb3fb4f3Stomee 454fb3fb4f3Stomee /** 455fb3fb4f3Stomee * Compares the specified object with this distribution bucket 456fb3fb4f3Stomee * for equality. Defines equality of two distribution buckets 457fb3fb4f3Stomee * as having the same range and the same frequency. 458fb3fb4f3Stomee * 459fb3fb4f3Stomee * @return false if the specified object is not a {@code 460fb3fb4f3Stomee * Distribution.Bucket} 461fb3fb4f3Stomee */ 462fb3fb4f3Stomee @Override 463fb3fb4f3Stomee public boolean equals(Object o)464fb3fb4f3Stomee equals(Object o) 465fb3fb4f3Stomee { 466fb3fb4f3Stomee if (o instanceof Bucket) { 467fb3fb4f3Stomee Bucket b = (Bucket)o; 468fb3fb4f3Stomee return ((min == b.min) && 469fb3fb4f3Stomee (max == b.max) && 470fb3fb4f3Stomee (frequency == b.frequency)); 471fb3fb4f3Stomee } 472fb3fb4f3Stomee return false; 473fb3fb4f3Stomee } 474fb3fb4f3Stomee 475fb3fb4f3Stomee /** 476fb3fb4f3Stomee * Overridden to ensure that equal buckets have equal hashcodes. 477fb3fb4f3Stomee */ 478fb3fb4f3Stomee @Override 479fb3fb4f3Stomee public int hashCode()480fb3fb4f3Stomee hashCode() 481fb3fb4f3Stomee { 482fb3fb4f3Stomee int hash = 17; 483fb3fb4f3Stomee hash = (37 * hash) + ((int)(min ^ (min >>> 32))); 484fb3fb4f3Stomee hash = (37 * hash) + ((int)(max ^ (max >>> 32))); 485fb3fb4f3Stomee hash = (37 * hash) + ((int)(frequency ^ (frequency >>> 32))); 486fb3fb4f3Stomee return hash; 487fb3fb4f3Stomee } 488fb3fb4f3Stomee 489fb3fb4f3Stomee private void readObject(ObjectInputStream s)490fb3fb4f3Stomee readObject(ObjectInputStream s) 491fb3fb4f3Stomee throws IOException, ClassNotFoundException 492fb3fb4f3Stomee { 493fb3fb4f3Stomee s.defaultReadObject(); 494fb3fb4f3Stomee // check class invariants (as constructor does) 495fb3fb4f3Stomee if (max < min) { 496fb3fb4f3Stomee throw new InvalidObjectException("upper bound " + 497fb3fb4f3Stomee max + " is less than lower bound " + min); 498fb3fb4f3Stomee } 499fb3fb4f3Stomee } 500fb3fb4f3Stomee 501fb3fb4f3Stomee /** 502fb3fb4f3Stomee * Gets a string representation of this distribution bucket 503fb3fb4f3Stomee * useful for logging and not intended for display. The exact 504fb3fb4f3Stomee * details of the representation are unspecified and subject to 505fb3fb4f3Stomee * change, but the following format may be regarded as typical: 506fb3fb4f3Stomee * <pre><code> 507fb3fb4f3Stomee * class-name[property1 = value1, property2 = value2] 508fb3fb4f3Stomee * </code></pre> 509fb3fb4f3Stomee */ 510fb3fb4f3Stomee public String toString()511fb3fb4f3Stomee toString() 512fb3fb4f3Stomee { 5134ae67516Stomee StringBuilder buf = new StringBuilder(); 514fb3fb4f3Stomee buf.append(Bucket.class.getName()); 515fb3fb4f3Stomee buf.append("[min = "); 516fb3fb4f3Stomee buf.append(min); 517fb3fb4f3Stomee buf.append(", max = "); 518fb3fb4f3Stomee buf.append(max); 519fb3fb4f3Stomee buf.append(", frequency = "); 520fb3fb4f3Stomee buf.append(frequency); 521fb3fb4f3Stomee buf.append(']'); 522fb3fb4f3Stomee return buf.toString(); 523fb3fb4f3Stomee } 524fb3fb4f3Stomee } 525fb3fb4f3Stomee 526fb3fb4f3Stomee /** 527fb3fb4f3Stomee * Gets a list of buckets of interest by excluding empty buckets at 528fb3fb4f3Stomee * both ends of the distribution. Leaves one empty bucket on each 529fb3fb4f3Stomee * end if possible to convey the distribution context more 530fb3fb4f3Stomee * effectively in a display. 531fb3fb4f3Stomee * 532fb3fb4f3Stomee * @return an unmodifiable sublist that includes the range starting 533fb3fb4f3Stomee * from the first bucket with a non-zero frequency and ending with 534fb3fb4f3Stomee * the last bucket with a non-zero frequency, plus one empty bucket 535fb3fb4f3Stomee * before and after that range if possible 536fb3fb4f3Stomee */ 537fb3fb4f3Stomee public List <Bucket> getDisplayRange()538fb3fb4f3Stomee getDisplayRange() 539fb3fb4f3Stomee { 540fb3fb4f3Stomee checkInit(); 541fb3fb4f3Stomee int min = -1; 542fb3fb4f3Stomee int max = -1; 543fb3fb4f3Stomee int len = size(); 544fb3fb4f3Stomee Bucket b; 545fb3fb4f3Stomee // Get first non-empty bucket 546fb3fb4f3Stomee for (int i = 0; i < len; ++i) { 547fb3fb4f3Stomee b = buckets.get(i); 548fb3fb4f3Stomee if (b.getFrequency() > 0L) { 549fb3fb4f3Stomee min = i; 550fb3fb4f3Stomee break; 551fb3fb4f3Stomee } 552fb3fb4f3Stomee } 553fb3fb4f3Stomee if (min < 0) { 554fb3fb4f3Stomee return Collections. <Bucket> emptyList(); 555fb3fb4f3Stomee } 556fb3fb4f3Stomee // Get last non-empty bucket 557fb3fb4f3Stomee for (int i = (len - 1); i >= 0; --i) { 558fb3fb4f3Stomee b = buckets.get(i); 559fb3fb4f3Stomee if (b.getFrequency() > 0L) { 560fb3fb4f3Stomee max = i; 561fb3fb4f3Stomee break; 562fb3fb4f3Stomee } 563fb3fb4f3Stomee } 564fb3fb4f3Stomee // If possible, pad non-empty range with one empty bucket at 565fb3fb4f3Stomee // each end. 566fb3fb4f3Stomee if (min > 0) { 567fb3fb4f3Stomee --min; 568fb3fb4f3Stomee } 569fb3fb4f3Stomee if (max < (len - 1)) { 570fb3fb4f3Stomee ++max; 571fb3fb4f3Stomee } 572fb3fb4f3Stomee 573fb3fb4f3Stomee // subList inclusive at low index, exclusive at high index 574fb3fb4f3Stomee return Collections. <Bucket> 575fb3fb4f3Stomee unmodifiableList(buckets).subList(min, max + 1); 576fb3fb4f3Stomee } 577fb3fb4f3Stomee 578fb3fb4f3Stomee private void readObject(ObjectInputStream s)579fb3fb4f3Stomee readObject(ObjectInputStream s) 580fb3fb4f3Stomee throws IOException, ClassNotFoundException 581fb3fb4f3Stomee { 582fb3fb4f3Stomee s.defaultReadObject(); 583fb3fb4f3Stomee 584fb3fb4f3Stomee // Defensively copy buckets _before_ validating. Subclass 585fb3fb4f3Stomee // validates by calling initialize() after reading any state 586fb3fb4f3Stomee // specific to that subclass needed for validation. 587fb3fb4f3Stomee int len = buckets.size(); 588fb3fb4f3Stomee ArrayList <Bucket> copy = new ArrayList <Bucket> (len); 589fb3fb4f3Stomee copy.addAll(buckets); 590fb3fb4f3Stomee buckets = copy; 591fb3fb4f3Stomee } 592fb3fb4f3Stomee 593fb3fb4f3Stomee /** 594fb3fb4f3Stomee * Gets a string representation of this {@code Distribution} useful 595fb3fb4f3Stomee * for logging and not intended for display. The exact details of 596fb3fb4f3Stomee * the representation are unspecified and subject to change, but the 597fb3fb4f3Stomee * following format may be regarded as typical: 598fb3fb4f3Stomee * <pre><code> 599fb3fb4f3Stomee * class-name[property1 = value1, property2 = value2] 600fb3fb4f3Stomee * </code></pre> 601fb3fb4f3Stomee */ 602fb3fb4f3Stomee public String toString()603fb3fb4f3Stomee toString() 604fb3fb4f3Stomee { 605fb3fb4f3Stomee checkInit(); 6064ae67516Stomee StringBuilder buf = new StringBuilder(); 607fb3fb4f3Stomee buf.append(Distribution.class.getName()); 608fb3fb4f3Stomee buf.append("[buckets = "); 609fb3fb4f3Stomee List <Bucket> list = getDisplayRange(); 610fb3fb4f3Stomee if (list.isEmpty()) { 611fb3fb4f3Stomee buf.append("<empty>"); 612fb3fb4f3Stomee } else { 613fb3fb4f3Stomee buf.append(Arrays.toString(getDisplayRange().toArray())); 614fb3fb4f3Stomee } 615fb3fb4f3Stomee buf.append(", total = "); 616fb3fb4f3Stomee buf.append(getTotal()); 617fb3fb4f3Stomee buf.append(']'); 618fb3fb4f3Stomee return buf.toString(); 619fb3fb4f3Stomee } 620fb3fb4f3Stomee } 621