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 /* 23e77b06d2Stomee * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 24fb3fb4f3Stomee * Use is subject to license terms. 25fb3fb4f3Stomee */ 26fb3fb4f3Stomee package org.opensolaris.os.dtrace; 27fb3fb4f3Stomee 28fb3fb4f3Stomee import java.io.*; 29fb3fb4f3Stomee import java.beans.*; 30fb3fb4f3Stomee import java.util.*; 31fb3fb4f3Stomee 32fb3fb4f3Stomee /** 33fb3fb4f3Stomee * A record generated by the DTrace {@code printa()} action. Lists the 34fb3fb4f3Stomee * aggregations passed to {@code printa()} and records the formatted 35fb3fb4f3Stomee * output associated with each {@link Tuple}. If multiple aggregations 36fb3fb4f3Stomee * were passed to the {@code printa()} action that generated this 37fb3fb4f3Stomee * record, then the DTrace library tabulates the output, using a default 38fb3fb4f3Stomee * format if no format string was specified. By default, the output 39fb3fb4f3Stomee * string associated with a given {@code Tuple} includes a value from 40fb3fb4f3Stomee * each aggregation, or zero wherever an aggregation has no value 41fb3fb4f3Stomee * associated with that {@code Tuple}. For example, the D statements 42fb3fb4f3Stomee * <pre><code> 43fb3fb4f3Stomee * @a[123] = sum(1); 44fb3fb4f3Stomee * @b[456] = sum(2); 45fb3fb4f3Stomee * printa(@a, @b, @c); 46fb3fb4f3Stomee * </code></pre> 47fb3fb4f3Stomee * produce output for the tuples "123" and "456" similar to the 48fb3fb4f3Stomee * following: 49fb3fb4f3Stomee * <pre><code> 50fb3fb4f3Stomee * 123 1 0 0 51fb3fb4f3Stomee * 456 0 2 0 52fb3fb4f3Stomee * </code></pre> 53fb3fb4f3Stomee * The first column after the tuple contains values from {@code @a}, 54fb3fb4f3Stomee * the next column contains values from {@code @b}, and the last 55fb3fb4f3Stomee * column contains zeros because {@code @c} has neither a value 56fb3fb4f3Stomee * associated with "123" nor a value associated with "456". 57fb3fb4f3Stomee * <p> 58fb3fb4f3Stomee * If a format string is passed to {@code printa()}, it may limit the 59fb3fb4f3Stomee * aggregation data available in this record. For example, if the 60fb3fb4f3Stomee * format string specifies a value placeholder for only one of two 61fb3fb4f3Stomee * aggregations passed to {@code printa()}, then the resulting {@code 62fb3fb4f3Stomee * PrintaRecord} will contain only one {@code Aggregation}. If no value 63fb3fb4f3Stomee * placeholder is specified, or if the aggregation tuple is not 64fb3fb4f3Stomee * completely specified, the resulting {@code PrintaRecord} will contain 65fb3fb4f3Stomee * no aggregation data. However, the formatted output generated by the 66fb3fb4f3Stomee * DTrace library is available in all cases. For details about 67fb3fb4f3Stomee * {@code printa()} format strings, see the <a 68*3a931819SPeter Tribble * href=http://dtrace.org/guide/chp-fmt.html#chp-fmt-printa> 69fb3fb4f3Stomee * <b>{@code printa()}</b></a> section of the <b>Output 70*3a931819SPeter Tribble * Formatting</b> chapter of the <i>Dynamic Tracing Guide</i>. 71fb3fb4f3Stomee * <p> 72fb3fb4f3Stomee * Immutable. Supports persistence using {@link java.beans.XMLEncoder}. 73fb3fb4f3Stomee * 74fb3fb4f3Stomee * @author Tom Erickson 75fb3fb4f3Stomee */ 76e77b06d2Stomee public final class PrintaRecord implements Record, Serializable, 77e77b06d2Stomee Comparable <PrintaRecord> { 78fb3fb4f3Stomee static final long serialVersionUID = -4174277639915895694L; 79fb3fb4f3Stomee 80fb3fb4f3Stomee static { 81fb3fb4f3Stomee try { 82fb3fb4f3Stomee BeanInfo info = Introspector.getBeanInfo(PrintaRecord.class); 83fb3fb4f3Stomee PersistenceDelegate persistenceDelegate = 84fb3fb4f3Stomee new DefaultPersistenceDelegate( 85fb3fb4f3Stomee new String[] {"snaptime", "aggregations", 86e77b06d2Stomee "formattedStrings", "tuples", "output"}) 87e77b06d2Stomee { 88e77b06d2Stomee /* 89e77b06d2Stomee * Need to prevent DefaultPersistenceDelegate from using 90e77b06d2Stomee * overridden equals() method, resulting in a 91e77b06d2Stomee * StackOverFlowError. Revert to PersistenceDelegate 92e77b06d2Stomee * implementation. See 93e77b06d2Stomee * http://forum.java.sun.com/thread.jspa?threadID= 94e77b06d2Stomee * 477019&tstart=135 95e77b06d2Stomee */ 96e77b06d2Stomee protected boolean 97e77b06d2Stomee mutatesTo(Object oldInstance, Object newInstance) 98e77b06d2Stomee { 99e77b06d2Stomee return (newInstance != null && oldInstance != null && 100e77b06d2Stomee oldInstance.getClass() == newInstance.getClass()); 101e77b06d2Stomee } 102e77b06d2Stomee }; 103fb3fb4f3Stomee BeanDescriptor d = info.getBeanDescriptor(); 104fb3fb4f3Stomee d.setValue("persistenceDelegate", persistenceDelegate); 105fb3fb4f3Stomee } catch (IntrospectionException e) { 106fb3fb4f3Stomee System.out.println(e); 107fb3fb4f3Stomee } 108fb3fb4f3Stomee } 109fb3fb4f3Stomee 110fb3fb4f3Stomee /** @serial */ 111fb3fb4f3Stomee private final long snaptime; 112fb3fb4f3Stomee /** @serial */ 113fb3fb4f3Stomee private List <Aggregation> aggregations; 114fb3fb4f3Stomee /** @serial */ 115fb3fb4f3Stomee private Map <Tuple, String> formattedStrings; 116fb3fb4f3Stomee /** @serial */ 117fb3fb4f3Stomee private List <Tuple> tuples; 1184ae67516Stomee private transient StringBuilder outputBuffer; 119fb3fb4f3Stomee private transient String output; 120fb3fb4f3Stomee private transient boolean formatted; 121fb3fb4f3Stomee 122fb3fb4f3Stomee /** 123fb3fb4f3Stomee * Package level access, called by ProbeData. 124fb3fb4f3Stomee */ PrintaRecord(long snaptimeNanos, boolean isFormatString)125fb3fb4f3Stomee PrintaRecord(long snaptimeNanos, boolean isFormatString) 126fb3fb4f3Stomee { 127fb3fb4f3Stomee snaptime = snaptimeNanos; 128fb3fb4f3Stomee aggregations = new ArrayList <Aggregation> (); 129fb3fb4f3Stomee formattedStrings = new HashMap <Tuple, String> (); 130fb3fb4f3Stomee tuples = new ArrayList <Tuple> (); 1314ae67516Stomee outputBuffer = new StringBuilder(); 132fb3fb4f3Stomee formatted = isFormatString; 133fb3fb4f3Stomee validate(); 134fb3fb4f3Stomee } 135fb3fb4f3Stomee 136fb3fb4f3Stomee /** 137fb3fb4f3Stomee * Creates a record with the given snaptime, aggregations, and 138fb3fb4f3Stomee * formatted output. 139fb3fb4f3Stomee * 140fb3fb4f3Stomee * @param snaptimeNanos nanosecond timestamp of the snapshot used 141fb3fb4f3Stomee * to create this {@code printa()} record 142fb3fb4f3Stomee * @param aggs aggregations passed to the {@code printa()} action 143fb3fb4f3Stomee * that generated this record 144fb3fb4f3Stomee * @param formattedOutput the formatted output, if any, associated 145fb3fb4f3Stomee * with each {@code Tuple} occurring in the aggregations belonging 146fb3fb4f3Stomee * to this record, one formatted string per {@code Tuple}, or an 147fb3fb4f3Stomee * empty or {@code null} map if an incomplete {@code printa()} 148fb3fb4f3Stomee * format string caused aggregation tuples to be omitted from this 149fb3fb4f3Stomee * record 150fb3fb4f3Stomee * @param orderedTuples list of aggregation tuples in the same order 151fb3fb4f3Stomee * generated by the native DTrace library (determined by the various 152fb3fb4f3Stomee * "aggsort" options such as {@link Option#aggsortkey}) 153fb3fb4f3Stomee * @param formattedOutputString {@code printa()} formatted string 154fb3fb4f3Stomee * output in the same order generated by the native DTrace library 155fb3fb4f3Stomee * (determined by the various "aggsort" options such as 156fb3fb4f3Stomee * {@link Option#aggsortkey}) 157fb3fb4f3Stomee * @throws NullPointerException if the given collection of 158fb3fb4f3Stomee * aggregations is {@code null}, or if the given ordered lists of 159fb3fb4f3Stomee * tuples or formatted strings are {@code null} 160fb3fb4f3Stomee * @throws IllegalArgumentException if the given snaptime is 161fb3fb4f3Stomee * negative 162fb3fb4f3Stomee */ 163fb3fb4f3Stomee public PrintaRecord(long snaptimeNanos, Collection <Aggregation> aggs, Map <Tuple, String> formattedOutput, List <Tuple> orderedTuples, String formattedOutputString)164fb3fb4f3Stomee PrintaRecord(long snaptimeNanos, Collection <Aggregation> aggs, 165fb3fb4f3Stomee Map <Tuple, String> formattedOutput, 166fb3fb4f3Stomee List <Tuple> orderedTuples, 167fb3fb4f3Stomee String formattedOutputString) 168fb3fb4f3Stomee { 169fb3fb4f3Stomee snaptime = snaptimeNanos; 170fb3fb4f3Stomee if (aggs != null) { 171fb3fb4f3Stomee aggregations = new ArrayList <Aggregation> (aggs.size()); 172fb3fb4f3Stomee aggregations.addAll(aggs); 173fb3fb4f3Stomee } 174fb3fb4f3Stomee if (formattedOutput != null) { 175fb3fb4f3Stomee formattedStrings = new HashMap <Tuple, String> 176fb3fb4f3Stomee (formattedOutput); 177fb3fb4f3Stomee } 178fb3fb4f3Stomee if (orderedTuples != null) { 179fb3fb4f3Stomee tuples = new ArrayList <Tuple> (orderedTuples.size()); 180fb3fb4f3Stomee tuples.addAll(orderedTuples); 181fb3fb4f3Stomee } 182fb3fb4f3Stomee output = formattedOutputString; 183fb3fb4f3Stomee validate(); 184fb3fb4f3Stomee } 185fb3fb4f3Stomee 18691cfa10aStomee private final void validate()187fb3fb4f3Stomee validate() 188fb3fb4f3Stomee { 189fb3fb4f3Stomee if (snaptime < 0) { 190fb3fb4f3Stomee throw new IllegalArgumentException("snaptime is negative"); 191fb3fb4f3Stomee } 192fb3fb4f3Stomee if (aggregations == null) { 193fb3fb4f3Stomee throw new NullPointerException("aggregations list is null"); 194fb3fb4f3Stomee } 195fb3fb4f3Stomee Aggregation a; 196fb3fb4f3Stomee for (int i = 0, len = aggregations.size(); i < len; ++i) { 197fb3fb4f3Stomee a = aggregations.get(i); 198fb3fb4f3Stomee if (a == null) { 199fb3fb4f3Stomee throw new NullPointerException( 200fb3fb4f3Stomee "null aggregation at index " + i); 201fb3fb4f3Stomee } 202fb3fb4f3Stomee } 203fb3fb4f3Stomee if (tuples == null) { 204fb3fb4f3Stomee throw new NullPointerException("ordered tuple list is null"); 205fb3fb4f3Stomee } 206fb3fb4f3Stomee if (output == null && outputBuffer == null) { 207fb3fb4f3Stomee throw new NullPointerException("formatted output is null"); 208fb3fb4f3Stomee } 209fb3fb4f3Stomee } 210fb3fb4f3Stomee 211fb3fb4f3Stomee /** 212fb3fb4f3Stomee * Gets the nanosecond timestamp of the aggregate snapshot used to 213fb3fb4f3Stomee * create this {@code printa()} record. 214fb3fb4f3Stomee * 215fb3fb4f3Stomee * @return nanosecond timestamp 216fb3fb4f3Stomee */ 217fb3fb4f3Stomee public long getSnaptime()218fb3fb4f3Stomee getSnaptime() 219fb3fb4f3Stomee { 220fb3fb4f3Stomee return snaptime; 221fb3fb4f3Stomee } 222fb3fb4f3Stomee 223fb3fb4f3Stomee private Aggregation getAggregationImpl(String name)224fb3fb4f3Stomee getAggregationImpl(String name) 225fb3fb4f3Stomee { 226fb3fb4f3Stomee if (name == null) { 227fb3fb4f3Stomee return null; 228fb3fb4f3Stomee } 229fb3fb4f3Stomee for (Aggregation a : aggregations) { 230fb3fb4f3Stomee if (name.equals(a.getName())) { 231fb3fb4f3Stomee return a; 232fb3fb4f3Stomee } 233fb3fb4f3Stomee } 234fb3fb4f3Stomee return null; 235fb3fb4f3Stomee } 236fb3fb4f3Stomee 237fb3fb4f3Stomee /** 238fb3fb4f3Stomee * Gets the named aggregation. 239fb3fb4f3Stomee * 240fb3fb4f3Stomee * @return the named aggregation passed to {@code printa()}, or 241fb3fb4f3Stomee * {@code null} if the named aggregation is not passed to {@code 242fb3fb4f3Stomee * printa()}, or if it is omitted due to an incomplete {@code 243fb3fb4f3Stomee * printa()} format string, or if it is empty (a future release of 244fb3fb4f3Stomee * this API may represent an empty DTrace aggregation as a non-null 245fb3fb4f3Stomee * {@code Aggregation} with no records; users of this API should not 246fb3fb4f3Stomee * rely on a non-null return value to indicate a non-zero record 247fb3fb4f3Stomee * count) 248fb3fb4f3Stomee */ 249fb3fb4f3Stomee public Aggregation getAggregation(String name)250fb3fb4f3Stomee getAggregation(String name) 251fb3fb4f3Stomee { 252fb3fb4f3Stomee name = Aggregate.filterUnnamedAggregationName(name); 253fb3fb4f3Stomee return getAggregationImpl(name); 254fb3fb4f3Stomee } 255fb3fb4f3Stomee 256fb3fb4f3Stomee /** 257fb3fb4f3Stomee * Gets a list of the aggregations passed to the {@code printa()} 258fb3fb4f3Stomee * action that generated this record. The returned list is a copy, 259fb3fb4f3Stomee * and modifying it has no effect on this record. Supports XML 260fb3fb4f3Stomee * persistence. 261fb3fb4f3Stomee * 262fb3fb4f3Stomee * @return non-null, possibly empty list of aggregations belonging 263fb3fb4f3Stomee * to this record (empty aggregations are excluded) 264fb3fb4f3Stomee */ 265fb3fb4f3Stomee public List <Aggregation> getAggregations()266fb3fb4f3Stomee getAggregations() 267fb3fb4f3Stomee { 268fb3fb4f3Stomee return new ArrayList <Aggregation> (aggregations); 269fb3fb4f3Stomee } 270fb3fb4f3Stomee 271fb3fb4f3Stomee /** 272fb3fb4f3Stomee * Gets the formatted string, if any, associated with the given 273fb3fb4f3Stomee * aggregation tuple. 274fb3fb4f3Stomee * 275fb3fb4f3Stomee * @param key aggregation tuple 276fb3fb4f3Stomee * @return the formatted string associated with the given 277fb3fb4f3Stomee * aggregation tuple, or {@code null} if the given tuple does not 278fb3fb4f3Stomee * exist in the aggregations belonging to this record or if it 279fb3fb4f3Stomee * is omitted from this record due to an incomplete {@code printa()} 280fb3fb4f3Stomee * format string 281fb3fb4f3Stomee * @see #getFormattedStrings() 282fb3fb4f3Stomee * @see #getOutput() 283fb3fb4f3Stomee */ 284fb3fb4f3Stomee public String getFormattedString(Tuple key)285fb3fb4f3Stomee getFormattedString(Tuple key) 286fb3fb4f3Stomee { 287fb3fb4f3Stomee if (formattedStrings == null) { 288fb3fb4f3Stomee return null; 289fb3fb4f3Stomee } 290fb3fb4f3Stomee return formattedStrings.get(key); 291fb3fb4f3Stomee } 292fb3fb4f3Stomee 293fb3fb4f3Stomee /** 294fb3fb4f3Stomee * Gets the formatted output, if any, associated with each {@code 295fb3fb4f3Stomee * Tuple} occurring in the aggregations belonging to this record, 296fb3fb4f3Stomee * one formatted string per {@code Tuple}. Gets an empty map if 297fb3fb4f3Stomee * aggregation tuples are omitted from this record due to an 298fb3fb4f3Stomee * incomplete {@code printa()} format string. The returned map is a 299fb3fb4f3Stomee * copy and modifying it has no effect on this record. Supports XML 300fb3fb4f3Stomee * persistence. 301fb3fb4f3Stomee * 302fb3fb4f3Stomee * @return a map of aggregation tuples and their associated 303fb3fb4f3Stomee * formatted output strings, empty if aggregation tuples are omitted 304fb3fb4f3Stomee * from this record due to an incomplete {@code printa(}) format 305fb3fb4f3Stomee * string 306fb3fb4f3Stomee * @see #getFormattedString(Tuple key) 307fb3fb4f3Stomee * @see #getOutput() 308fb3fb4f3Stomee */ 309fb3fb4f3Stomee public Map <Tuple, String> getFormattedStrings()310fb3fb4f3Stomee getFormattedStrings() 311fb3fb4f3Stomee { 312fb3fb4f3Stomee if (formattedStrings == null) { 313fb3fb4f3Stomee return new HashMap <Tuple, String> (); 314fb3fb4f3Stomee } 315fb3fb4f3Stomee return new HashMap <Tuple, String> (formattedStrings); 316fb3fb4f3Stomee } 317fb3fb4f3Stomee 318fb3fb4f3Stomee /** 319fb3fb4f3Stomee * Gets an ordered list of this record's aggregation tuples. The 320fb3fb4f3Stomee * returned list is a copy, and modifying it has no effect on this 321fb3fb4f3Stomee * record. Supports XML persistence. 322fb3fb4f3Stomee * 323fb3fb4f3Stomee * @return a non-null list of this record's aggregation tuples in 324fb3fb4f3Stomee * the order they were generated by the native DTrace library, as 325fb3fb4f3Stomee * determined by the {@link Option#aggsortkey}, {@link 326fb3fb4f3Stomee * Option#aggsortrev}, {@link Option#aggsortpos}, and {@link 327fb3fb4f3Stomee * Option#aggsortkeypos} options 328fb3fb4f3Stomee */ 329fb3fb4f3Stomee public List <Tuple> getTuples()330fb3fb4f3Stomee getTuples() 331fb3fb4f3Stomee { 332fb3fb4f3Stomee return new ArrayList <Tuple> (tuples); 333fb3fb4f3Stomee } 334fb3fb4f3Stomee 335fb3fb4f3Stomee /** 336fb3fb4f3Stomee * Gets this record's formatted output. Supports XML persistence. 337fb3fb4f3Stomee * 338fb3fb4f3Stomee * @return non-null formatted output in the order generated by the 339fb3fb4f3Stomee * native DTrace library, as determined by the {@link 340fb3fb4f3Stomee * Option#aggsortkey}, {@link Option#aggsortrev}, {@link 341fb3fb4f3Stomee * Option#aggsortpos}, and {@link Option#aggsortkeypos} options 342fb3fb4f3Stomee */ 343fb3fb4f3Stomee public String getOutput()344fb3fb4f3Stomee getOutput() 345fb3fb4f3Stomee { 346fb3fb4f3Stomee if (output == null) { 347fb3fb4f3Stomee output = outputBuffer.toString(); 348fb3fb4f3Stomee outputBuffer = null; 349fb3fb4f3Stomee if ((output.length() == 0) && !formatted) { 350fb3fb4f3Stomee output = "\n"; 351fb3fb4f3Stomee } 352fb3fb4f3Stomee } 353fb3fb4f3Stomee return output; 354fb3fb4f3Stomee } 355fb3fb4f3Stomee 356fb3fb4f3Stomee /** 357fb3fb4f3Stomee * Package level access, called by ProbeData. 358fb3fb4f3Stomee * 359fb3fb4f3Stomee * @throws NullPointerException if aggregationName is null 360fb3fb4f3Stomee * @throws IllegalStateException if this PrintaRecord has an 361fb3fb4f3Stomee * aggregation matching the given name and it already has an 362fb3fb4f3Stomee * AggregationRecord with the same tuple key as the given record. 363fb3fb4f3Stomee */ 364fb3fb4f3Stomee void addRecord(String aggregationName, long aggid, AggregationRecord record)365fb3fb4f3Stomee addRecord(String aggregationName, long aggid, AggregationRecord record) 366fb3fb4f3Stomee { 367fb3fb4f3Stomee if (formattedStrings == null) { 368fb3fb4f3Stomee // printa() format string does not completely specify tuple 369fb3fb4f3Stomee return; 370fb3fb4f3Stomee } 371fb3fb4f3Stomee 372fb3fb4f3Stomee aggregationName = Aggregate.filterUnnamedAggregationName( 373fb3fb4f3Stomee aggregationName); 374fb3fb4f3Stomee Aggregation aggregation = getAggregationImpl(aggregationName); 375fb3fb4f3Stomee if (aggregation == null) { 376fb3fb4f3Stomee aggregation = new Aggregation(aggregationName, aggid); 377fb3fb4f3Stomee aggregations.add(aggregation); 378fb3fb4f3Stomee } 379fb3fb4f3Stomee try { 380fb3fb4f3Stomee aggregation.addRecord(record); 381fb3fb4f3Stomee } catch (IllegalArgumentException e) { 382fb3fb4f3Stomee Map <Tuple, AggregationRecord> map = aggregation.asMap(); 383fb3fb4f3Stomee AggregationRecord r = map.get(record.getTuple()); 384fb3fb4f3Stomee // 385fb3fb4f3Stomee // The printa() format string may specify the value of the 386fb3fb4f3Stomee // aggregating action multiple times. While that changes 387fb3fb4f3Stomee // the resulting formatted string associated with the tuple, 388fb3fb4f3Stomee // we ignore the attempt to add the redundant record to the 389fb3fb4f3Stomee // aggregation. 390fb3fb4f3Stomee // 391fb3fb4f3Stomee if (!r.equals(record)) { 392fb3fb4f3Stomee throw e; 393fb3fb4f3Stomee } 394fb3fb4f3Stomee } 395fb3fb4f3Stomee } 396fb3fb4f3Stomee 397fb3fb4f3Stomee // 398fb3fb4f3Stomee // Called from native code when the tuple is not completely 399fb3fb4f3Stomee // specified in the printa() format string. 400fb3fb4f3Stomee // 401fb3fb4f3Stomee void invalidate()402fb3fb4f3Stomee invalidate() 403fb3fb4f3Stomee { 404fb3fb4f3Stomee formattedStrings = null; 405fb3fb4f3Stomee aggregations.clear(); 406fb3fb4f3Stomee tuples.clear(); 407fb3fb4f3Stomee } 408fb3fb4f3Stomee 409fb3fb4f3Stomee void addFormattedString(Tuple tuple, String formattedString)410fb3fb4f3Stomee addFormattedString(Tuple tuple, String formattedString) 411fb3fb4f3Stomee { 412fb3fb4f3Stomee if (tuple != null && formattedStrings != null) { 413fb3fb4f3Stomee if (formattedStrings.containsKey(tuple)) { 414fb3fb4f3Stomee throw new IllegalArgumentException("A formatted string " + 415fb3fb4f3Stomee "for tuple " + tuple + " already exists."); 416fb3fb4f3Stomee } else { 417fb3fb4f3Stomee formattedStrings.put(tuple, formattedString); 418fb3fb4f3Stomee tuples.add(tuple); 419fb3fb4f3Stomee } 420fb3fb4f3Stomee } 421fb3fb4f3Stomee outputBuffer.append(formattedString); 422fb3fb4f3Stomee } 423fb3fb4f3Stomee 424e77b06d2Stomee /** 425e77b06d2Stomee * Compares the specified object with this {@code PrintaRecord} for 426e77b06d2Stomee * equality. Returns {@code true} if and only if the specified 427e77b06d2Stomee * object is also a {@code PrintaRecord} and both records have the 428e77b06d2Stomee * same aggregations and the same formatted strings in the same 429e77b06d2Stomee * order (by aggregation tuple). 430e77b06d2Stomee * 431e77b06d2Stomee * @return {@code true} if and only if the specified object is also 432e77b06d2Stomee * a {@code PrintaRecord} and both records have the same 433e77b06d2Stomee * aggregations and the same formatted strings in the same order (by 434e77b06d2Stomee * aggregation tuple) 435e77b06d2Stomee */ 436e77b06d2Stomee @Override 437e77b06d2Stomee public boolean equals(Object o)438e77b06d2Stomee equals(Object o) 439e77b06d2Stomee { 440e77b06d2Stomee if (o instanceof PrintaRecord) { 441e77b06d2Stomee PrintaRecord r = (PrintaRecord)o; 442e77b06d2Stomee return (aggregations.equals(r.aggregations) && 443e77b06d2Stomee ((formattedStrings == null || formattedStrings.isEmpty()) 444e77b06d2Stomee ? (r.formattedStrings == null || 445e77b06d2Stomee r.formattedStrings.isEmpty()) 446e77b06d2Stomee : formattedStrings.equals(r.formattedStrings)) && 447e77b06d2Stomee tuples.equals(r.tuples)); 448e77b06d2Stomee } 449e77b06d2Stomee 450e77b06d2Stomee return false; 451e77b06d2Stomee } 452e77b06d2Stomee 453e77b06d2Stomee /** 454e77b06d2Stomee * Overridden to ensure that equal instances have equal hash codes. 455e77b06d2Stomee */ 456e77b06d2Stomee @Override 457e77b06d2Stomee public int hashCode()458e77b06d2Stomee hashCode() 459e77b06d2Stomee { 460e77b06d2Stomee int hash = 17; 461e77b06d2Stomee hash = (hash * 37) + aggregations.hashCode(); 462e77b06d2Stomee hash = (hash * 37) + ((formattedStrings == null || 463e77b06d2Stomee formattedStrings.isEmpty()) ? 0 : 464e77b06d2Stomee formattedStrings.hashCode()); 465e77b06d2Stomee hash = (hash * 37) + tuples.hashCode(); 466e77b06d2Stomee return hash; 467e77b06d2Stomee } 468e77b06d2Stomee 469e77b06d2Stomee /** 470e77b06d2Stomee * Compares the formatted {@link #getOutput() output} of this record 471e77b06d2Stomee * with that of the given record. Note that ordering {@code printa} 472e77b06d2Stomee * records by their output string values is incompatible with {@link 473e77b06d2Stomee * #equals(Object o) equals()}, which also checks the underlying 474e77b06d2Stomee * aggregation data for equality. 475e77b06d2Stomee * 476e77b06d2Stomee * @return a negative number, 0, or a positive number as this 477e77b06d2Stomee * record's formatted output is lexicographically less than, equal 478e77b06d2Stomee * to, or greater than the given record's formatted output 479e77b06d2Stomee */ 480e77b06d2Stomee public int compareTo(PrintaRecord r)481e77b06d2Stomee compareTo(PrintaRecord r) 482e77b06d2Stomee { 483e77b06d2Stomee return getOutput().compareTo(r.getOutput()); 484e77b06d2Stomee } 485e77b06d2Stomee 486fb3fb4f3Stomee /** 487fb3fb4f3Stomee * Serialize this {@code PrintaRecord} instance. 488fb3fb4f3Stomee * 489fb3fb4f3Stomee * @serialData Serialized fields are emitted, followed by the 490fb3fb4f3Stomee * formatted output string. 491fb3fb4f3Stomee */ 492fb3fb4f3Stomee private void writeObject(ObjectOutputStream s)493fb3fb4f3Stomee writeObject(ObjectOutputStream s) throws IOException 494fb3fb4f3Stomee { 495fb3fb4f3Stomee s.defaultWriteObject(); 496fb3fb4f3Stomee if (output == null) { 497fb3fb4f3Stomee s.writeObject(outputBuffer.toString()); 498fb3fb4f3Stomee } else { 499fb3fb4f3Stomee s.writeObject(output); 500fb3fb4f3Stomee } 501fb3fb4f3Stomee } 502fb3fb4f3Stomee 503fb3fb4f3Stomee private void readObject(ObjectInputStream s)504fb3fb4f3Stomee readObject(ObjectInputStream s) 505fb3fb4f3Stomee throws IOException, ClassNotFoundException 506fb3fb4f3Stomee { 507fb3fb4f3Stomee s.defaultReadObject(); 508fb3fb4f3Stomee output = (String)s.readObject(); 509fb3fb4f3Stomee // make defensive copy 510fb3fb4f3Stomee if (aggregations != null) { 511fb3fb4f3Stomee List <Aggregation> copy = new ArrayList <Aggregation> 512fb3fb4f3Stomee (aggregations.size()); 513fb3fb4f3Stomee copy.addAll(aggregations); 514fb3fb4f3Stomee aggregations = copy; 515fb3fb4f3Stomee } 516fb3fb4f3Stomee if (formattedStrings != null) { 517fb3fb4f3Stomee formattedStrings = new HashMap <Tuple, String> (formattedStrings); 518fb3fb4f3Stomee } 519fb3fb4f3Stomee if (tuples != null) { 520fb3fb4f3Stomee List <Tuple> copy = new ArrayList <Tuple> (tuples.size()); 521fb3fb4f3Stomee copy.addAll(tuples); 522fb3fb4f3Stomee tuples = copy; 523fb3fb4f3Stomee } 524*3a931819SPeter Tribble // check constructor invariants only after defensive copy 525fb3fb4f3Stomee try { 526fb3fb4f3Stomee validate(); 527fb3fb4f3Stomee } catch (Exception e) { 5284ae67516Stomee InvalidObjectException x = new InvalidObjectException( 5294ae67516Stomee e.getMessage()); 5304ae67516Stomee x.initCause(e); 5314ae67516Stomee throw x; 532fb3fb4f3Stomee } 533fb3fb4f3Stomee } 534fb3fb4f3Stomee 535fb3fb4f3Stomee /** 536fb3fb4f3Stomee * Gets a string representation of this instance useful for logging 537fb3fb4f3Stomee * and not intended for display. The exact details of the 538fb3fb4f3Stomee * representation are unspecified and subject to change, but the 539fb3fb4f3Stomee * following format may be regarded as typical: 540fb3fb4f3Stomee * <pre><code> 541fb3fb4f3Stomee * class-name[property1 = value1, property2 = value2] 542fb3fb4f3Stomee * </code></pre> 543fb3fb4f3Stomee */ 544fb3fb4f3Stomee public String toString()545fb3fb4f3Stomee toString() 546fb3fb4f3Stomee { 5474ae67516Stomee StringBuilder buf = new StringBuilder(); 548fb3fb4f3Stomee buf.append(PrintaRecord.class.getName()); 549fb3fb4f3Stomee buf.append("[snaptime = "); 550fb3fb4f3Stomee buf.append(snaptime); 551fb3fb4f3Stomee buf.append(", aggregations = "); 552fb3fb4f3Stomee buf.append(aggregations); 553fb3fb4f3Stomee buf.append(", formattedStrings = "); 554fb3fb4f3Stomee buf.append(formattedStrings); 555fb3fb4f3Stomee buf.append(", tuples = "); 556fb3fb4f3Stomee buf.append(tuples); 557fb3fb4f3Stomee buf.append(", output = "); 558fb3fb4f3Stomee buf.append(getOutput()); 559fb3fb4f3Stomee buf.append(']'); 560fb3fb4f3Stomee return buf.toString(); 561fb3fb4f3Stomee } 562fb3fb4f3Stomee } 563