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