1 /******************************************************************************* 2 * Copyright (c) 2000, 2009 IBM Corporation and others. 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/epl-v10.html 7 * 8 * Contributors: 9 * IBM Corporation - initial API and implementation 10 *******************************************************************************/ 11 package org.eclipse.test.internal.performance.results.utils; 12 13 import java.io.File; 14 import java.io.FileInputStream; 15 import java.io.FileNotFoundException; 16 import java.io.FileOutputStream; 17 import java.io.IOException; 18 import java.io.InputStream; 19 import java.io.OutputStream; 20 import java.text.NumberFormat; 21 import java.text.ParseException; 22 import java.text.SimpleDateFormat; 23 import java.util.ArrayList; 24 import java.util.Calendar; 25 import java.util.Comparator; 26 import java.util.Date; 27 import java.util.GregorianCalendar; 28 import java.util.List; 29 import java.util.Locale; 30 import java.util.StringTokenizer; 31 32 import org.eclipse.core.runtime.preferences.IEclipsePreferences; 33 import org.eclipse.test.internal.performance.results.db.BuildResults; 34 import org.eclipse.test.internal.performance.results.db.DB_Results; 35 36 /** 37 * Utility methods for statistics. Got from org.eclipse.test.performance 38 * framework 39 */ 40 public final class Util implements IPerformancesConstants { 41 42 // Percentages 43 public static final NumberFormat PERCENTAGE_FORMAT = NumberFormat.getPercentInstance(Locale.US); 44 static { 45 PERCENTAGE_FORMAT.setMaximumFractionDigits(2); 46 } 47 public static final NumberFormat DOUBLE_FORMAT = NumberFormat.getNumberInstance(Locale.US); 48 static { 49 DOUBLE_FORMAT.setMaximumFractionDigits(2); 50 } 51 52 // Strings 53 public static final String LINE_SEPARATOR = System.getProperty("line.separator"); 54 55 // Build prefixes 56 public static final List ALL_BUILD_PREFIXES = new ArrayList(3); 57 static { 58 ALL_BUILD_PREFIXES.add("I"); 59 ALL_BUILD_PREFIXES.add("N"); 60 ALL_BUILD_PREFIXES.add("M"); 61 } 62 public static final List BUILD_PREFIXES = new ArrayList(2); 63 static { 64 BUILD_PREFIXES.add("I"); 65 BUILD_PREFIXES.add("N"); 66 } 67 public static final List MAINTENANCE_BUILD_PREFIXES = new ArrayList(2); 68 static { 69 MAINTENANCE_BUILD_PREFIXES.add("I"); 70 MAINTENANCE_BUILD_PREFIXES.add("M"); 71 } 72 public static final List BASELINE_BUILD_PREFIXES = new ArrayList(1); 73 static { 74 BASELINE_BUILD_PREFIXES.add(DB_Results.getDbBaselinePrefix()); 75 } 76 77 // Milestones constants 78 private static String[] MILESTONES; 79 public static final BuildDateComparator BUILD_DATE_COMPARATOR = new BuildDateComparator(); 80 81 static class BuildDateComparator implements Comparator { 82 public int compare(Object o1, Object o2) { 83 String s1 = (String) o1; 84 String s2 = (String) o2; 85 return getBuildDate(s1).compareTo(getBuildDate(s2)); 86 } 87 } 88 89 private static void initMilestones() { 90 String version = DB_Results.getDbVersion(); 91 92 // Initialize reference version and database directory 93 char mainVersion = version.charAt(1); 94 char minorVersion = version.charAt(2); 95 96 // Initialize milestones 97 if (mainVersion == '3') { 98 switch (minorVersion) { 99 case '3': 100 case '4': 101 throw new RuntimeException("Version "+mainVersion+'.'+minorVersion+" is no longer supported!"); 102 case '5': 103 MILESTONES = V35_MILESTONES; 104 break; 105 case '6': 106 MILESTONES = V36_MILESTONES; 107 break; 108 default: 109 throw new RuntimeException("Version "+mainVersion+'.'+minorVersion+" is not supported yet!"); 110 } 111 } else { 112 throw new RuntimeException("Version "+mainVersion+'.'+minorVersion+" is not supported yet!"); 113 } 114 } 115 116 // Static information for time and date 117 public static final int ONE_MINUTE = 60000; 118 public static final long ONE_HOUR = 3600000L; 119 public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmm"); //$NON-NLS-1$ 120 121 /** 122 * Compute the student t-test values. 123 * 124 * @see "http://en.wikipedia.org/wiki/Student's_t-test" 125 * 126 * @param baselineResults The baseline build 127 * @param buildResults The current build 128 * @return The student t-test value as a double. 129 */ 130 public static double computeTTest(BuildResults baselineResults, BuildResults buildResults) { 131 132 double ref = baselineResults.getValue(); 133 double val = buildResults.getValue(); 134 135 double delta = ref - val; 136 long dfRef = baselineResults.getCount() - 1; 137 double sdRef = baselineResults.getDeviation(); 138 long dfVal = buildResults.getCount() - 1; 139 double sdVal = buildResults.getDeviation(); 140 // TODO if the stdev's are not sufficiently similar, we have to take a 141 // different approach 142 143 if (!Double.isNaN(sdRef) && !Double.isNaN(sdVal) && dfRef > 0 && dfVal > 0) { 144 long df = dfRef + dfVal; 145 double sp_square = (dfRef * sdRef * sdRef + dfVal * sdVal * sdVal) / df; 146 147 double se_diff = Math.sqrt(sp_square * (1.0 / (dfRef + 1) + 1.0 / (dfVal + 1))); 148 double t = Math.abs(delta / se_diff); 149 return t; 150 } 151 152 return -1; 153 } 154 155 /** 156 * Copy a file to another location. 157 * 158 * @param src the source file. 159 * @param dest the destination. 160 * @return <code>true</code> if the file was successfully copied, 161 * <code>false</code> otherwise. 162 */ 163 public static boolean copyFile(File src, File dest) { 164 165 try { 166 InputStream in = new FileInputStream(src); 167 OutputStream out = new FileOutputStream(dest); 168 byte[] buf = new byte[1024]; 169 int len; 170 while ((len = in.read(buf)) > 0) { 171 out.write(buf, 0, len); 172 } 173 in.close(); 174 out.close(); 175 } catch (FileNotFoundException e) { 176 e.printStackTrace(); 177 return false; 178 } catch (IOException e) { 179 e.printStackTrace(); 180 return false; 181 } 182 return true; 183 } 184 185 /** 186 * Copy a file content to another location. 187 * 188 * @param in the input stream. 189 * @param dest the destination. 190 * @return <code>true</code> if the file was successfully copied, 191 * <code>false</code> otherwise. 192 */ 193 public static boolean copyStream(InputStream in, File dest) { 194 195 try { 196 OutputStream out = new FileOutputStream(dest); 197 byte[] buf = new byte[1024]; 198 int len; 199 while ((len = in.read(buf)) > 0) { 200 out.write(buf, 0, len); 201 } 202 in.close(); 203 out.close(); 204 } catch (FileNotFoundException e) { 205 e.printStackTrace(); 206 return false; 207 } catch (IOException e) { 208 e.printStackTrace(); 209 return false; 210 } 211 return true; 212 } 213 214 /** 215 * Return the build date as yyyyMMddHHmm. 216 * 217 * @param buildName The build name (e.g. I20090806-0100) 218 * @return The date as a string. 219 */ 220 public static String getBuildDate(String buildName) { 221 return getBuildDate(buildName, DB_Results.getDbBaselinePrefix()); 222 } 223 224 /** 225 * Return the build date as yyyyMMddHHmm. 226 * 227 * @param buildName The build name (e.g. I20090806-0100) 228 * @param baselinePrefix The baseline prefix (e.g. {@link DB_Results#getDbBaselinePrefix()}) 229 * @return The date as a string. 230 */ 231 public static String getBuildDate(String buildName, String baselinePrefix) { 232 233 // Baseline name 234 if (baselinePrefix != null && buildName.startsWith(baselinePrefix)) { 235 int length = buildName.length(); 236 return buildName.substring(length-12, length); 237 } 238 239 // Build name 240 char first = buildName.charAt(0); 241 if (first == 'N' || first == 'I' || first == 'M') { // TODO (frederic) should be buildIdPrefixes... 242 return buildName.substring(1, 9)+buildName.substring(10, 14); 243 } 244 245 // Try with date format 246 int length = buildName.length() - 12 /* length of date */; 247 for (int i=0; i<=length; i++) { 248 try { 249 String substring = i == 0 ? buildName : buildName.substring(i); 250 Util.DATE_FORMAT.parse(substring); 251 return substring; // if no exception is raised then the substring has a correct date format => return it 252 } catch(ParseException ex) { 253 // skip 254 } 255 } 256 return null; 257 } 258 259 /** 260 * Returns the date of the milestone corresponding at the given index. 261 * 262 * @param index The index of the milestone 263 * @return The date as a YYYYMMDD-hhmm string. 264 */ 265 public static String getMilestoneDate(int index) { 266 int length = getMilestonesLength(); 267 if (index >= length) return null; 268 int dash = MILESTONES[index].indexOf('-'); 269 return MILESTONES[index].substring(dash+1); 270 } 271 272 /** 273 * Returns the milestone matching the given build name. 274 * 275 * @param buildName The name of the build 276 * @return The milestone as a string (e.g. M1) 277 */ 278 public static String getMilestone(String buildName) { 279 if (buildName != null && buildName.length() >= 12) { 280 int length = getMilestonesLength(); 281 String buildDate = getBuildDate(buildName, DB_Results.getDbBaselinePrefix()); 282 for (int i=0; i<length; i++) { 283 int start = MILESTONES[i].indexOf(buildDate); 284 if (start > 0) { 285 return MILESTONES[i]; 286 } 287 } 288 } 289 return null; 290 } 291 292 /** 293 * Returns the name the milestone matching the given build name. 294 * 295 * @param buildName The name of the build 296 * @return The milestone name as a string (e.g. M1) 297 */ 298 public static String getMilestoneName(String buildName) { 299 if (buildName != null && buildName.length() >= 12) { 300 int length = getMilestonesLength(); 301 String buildDate = getBuildDate(buildName, DB_Results.getDbBaselinePrefix()); 302 for (int i=0; i<length; i++) { 303 int start = MILESTONES[i].indexOf(buildDate); 304 if (start > 0) { 305 return MILESTONES[i].substring(0, start - 1); 306 } 307 } 308 } 309 return null; 310 } 311 312 /** 313 * Returns whether the given build name is a milestone or not. 314 * 315 * @param buildName The build name 316 * @return <code>true</code> if the build name matches a milestone one, 317 * <code>false</code> otherwise. 318 */ 319 public static boolean isMilestone(String buildName) { 320 return getMilestoneName(buildName) != null; 321 } 322 323 /** 324 * Returns the name of the milestone which run after the given build name 325 * or <code>null</code> if there's no milestone since the build has run. 326 * 327 * @param buildName The build name 328 * @return <code>true</code> if the build name matches a milestone one, 329 * <code>false</code> otherwise. 330 */ 331 public static String getNextMilestone(String buildName) { 332 int length = getMilestonesLength(); 333 String buildDate = getBuildDate(buildName); 334 for (int i=0; i<length; i++) { 335 String milestoneDate = MILESTONES[i].substring(MILESTONES[i].indexOf('-')+1); 336 if (milestoneDate.compareTo(buildDate) > 0) { 337 return milestoneDate; 338 } 339 } 340 return null; 341 } 342 343 /** 344 * Return the number of milestones. 345 * 346 * @return The number as an int 347 */ 348 public static int getMilestonesLength() { 349 if (MILESTONES == null) initMilestones(); 350 int length = MILESTONES.length; 351 return length; 352 } 353 354 /** 355 * @deprecated 356 */ 357 public static boolean matchPattern(String name, String pattern) { 358 if (pattern.equals("*")) return true; //$NON-NLS-1$ 359 if (pattern.indexOf('*') < 0 && pattern.indexOf('?') < 0) { 360 pattern += "*"; //$NON-NLS-1$ 361 } 362 StringTokenizer tokenizer = new StringTokenizer(pattern, "*?", true); //$NON-NLS-1$ 363 int start = 0; 364 String previous = ""; //$NON-NLS-1$ 365 while (tokenizer.hasMoreTokens()) { 366 String token = tokenizer.nextToken(); 367 if (!token.equals("*") && !token.equals("?")) { //$NON-NLS-1$ //$NON-NLS-2$ 368 if (previous.equals("*")) { //$NON-NLS-1$ 369 int idx = name.substring(start).indexOf(token); 370 if (idx < 0) return false; 371 start += idx; 372 } else { 373 if (previous.equals("?")) start++; //$NON-NLS-1$ 374 if (!name.substring(start).startsWith(token)) return false; 375 } 376 start += token.length(); 377 } 378 previous = token; 379 } 380 if (previous.equals("*")) { //$NON-NLS-1$ 381 return true; 382 } else if (previous.equals("?")) { //$NON-NLS-1$ 383 return name.length() == start; 384 } 385 return name.endsWith(previous); 386 } 387 388 /** 389 * @deprecated 390 */ 391 public static double round(double value) { 392 return Math.round(value * 10000) / 10000.0; 393 } 394 395 /** 396 * @deprecated 397 */ 398 public static double round(double value, int precision) { 399 if (precision < 0) { 400 throw new IllegalArgumentException("Should have a precision at least greater than 0!"); 401 } 402 if (precision == 0) return (long) Math.floor(value); 403 double factor = 10; 404 int n = 1; 405 while (n++ < precision) 406 factor *= 10; 407 return Math.round(value * factor) / factor; 408 } 409 410 /** 411 * Returns a string to display the given time as a duration 412 * formatted as "hh:mm:ss". 413 * 414 * @param time The time to format as a long. 415 * @return The formatted string. 416 */ 417 public static String timeChrono(long time) { 418 if (time < 1000) { // less than 1s 419 return "00:00:00"; //$NON-NLS-1$ 420 } 421 StringBuffer buffer = new StringBuffer(); 422 int seconds = (int) (time / 1000); 423 if (seconds < 60) { 424 buffer.append("00:00:"); //$NON-NLS-1$ 425 if (seconds < 10) buffer.append('0'); 426 buffer.append(seconds); 427 } else { 428 int minutes = seconds / 60; 429 if (minutes < 60) { 430 buffer.append("00:"); //$NON-NLS-1$ 431 if (minutes < 10) buffer.append('0'); 432 buffer.append(minutes); 433 buffer.append(':'); 434 seconds = seconds % 60; 435 if (seconds < 10) buffer.append('0'); 436 buffer.append(seconds); 437 } else { 438 int hours = minutes / 60; 439 if (hours < 10) buffer.append('0'); 440 buffer.append(hours); 441 buffer.append(':'); 442 minutes = minutes % 60; 443 if (minutes < 10) buffer.append('0'); 444 buffer.append(minutes); 445 buffer.append(':'); 446 seconds = seconds % 60; 447 if (seconds < 10) buffer.append('0'); 448 buffer.append(seconds); 449 } 450 } 451 return buffer.toString(); 452 } 453 454 /** 455 * Returns a string to display the given time as the hour of the day 456 * formatted as "hh:mm:ss". 457 * 458 * @param time The time to format as a long. 459 * @return The formatted string. 460 */ 461 public static String timeEnd(long time) { 462 GregorianCalendar calendar = new GregorianCalendar(); 463 calendar.add(Calendar.SECOND, (int)(time/1000)); 464 Date date = calendar.getTime(); 465 SimpleDateFormat dateFormat = new SimpleDateFormat("KK:mm:ss"); //$NON-NLS-1$ 466 return dateFormat.format(date); 467 } 468 469 /** 470 * Returns a string to display the given time as a duration 471 * formatted as: 472 * <ul> 473 * <li>"XXXms" if the duration is less than 0.1s (e.g. "543ms")</li> 474 * <li>"X.YYs" if the duration is less than 1s (e.g. "5.43s")</li> 475 * <li>"XX.Ys" if the duration is less than 1mn (e.g. "54.3s")</li> 476 * <li>"XXmn XXs" if the duration is less than 1h (e.g. "54mn 3s")</li> 477 * <li>"XXh XXmn XXs" if the duration is over than 1h (e.g. "5h 4mn 3s")</li> 478 * </ul> 479 * 480 * @param time The time to format as a long. 481 * @return The formatted string. 482 */ 483 public static String timeString(long time) { 484 NumberFormat format = NumberFormat.getInstance(); 485 format.setMaximumFractionDigits(1); 486 StringBuffer buffer = new StringBuffer(); 487 if (time == 0) { 488 // print nothing 489 } if (time < 100) { // less than 0.1s 490 buffer.append(time); 491 buffer.append("ms"); //$NON-NLS-1$ 492 } else if (time < 1000) { // less than 1s 493 if ((time%100) != 0) { 494 format.setMaximumFractionDigits(2); 495 } 496 buffer.append(format.format(time/1000.0)); 497 buffer.append("s"); //$NON-NLS-1$ 498 } else if (time < Util.ONE_MINUTE) { // less than 1mn 499 if ((time%1000) == 0) { 500 buffer.append(time/1000); 501 } else { 502 buffer.append(format.format(time/1000.0)); 503 } 504 buffer.append("s"); //$NON-NLS-1$ 505 } else if (time < Util.ONE_HOUR) { // less than 1h 506 buffer.append(time/Util.ONE_MINUTE).append("mn "); //$NON-NLS-1$ 507 long seconds = time%Util.ONE_MINUTE; 508 buffer.append(seconds/1000); 509 buffer.append("s"); //$NON-NLS-1$ 510 } else { // more than 1h 511 long h = time / Util.ONE_HOUR; 512 buffer.append(h).append("h "); //$NON-NLS-1$ 513 long m = (time % Util.ONE_HOUR) / Util.ONE_MINUTE; 514 buffer.append(m).append("mn "); //$NON-NLS-1$ 515 long seconds = m%Util.ONE_MINUTE; 516 buffer.append(seconds/1000); 517 buffer.append("s"); //$NON-NLS-1$ 518 } 519 return buffer.toString(); 520 } 521 522 private Util() { 523 // don't instantiate 524 } 525 526 /** 527 * Set the milestones. 528 * 529 * @param items The milestones list (e.g. {@link IPerformancesConstants#V35_MILESTONES}). 530 */ 531 public static void setMilestones(String[] items) { 532 MILESTONES = items; 533 } 534 535 /** 536 * Init the milestones from preferences 537 * 538 * @param preferences The preferences from which got milestones list 539 */ 540 public static void initMilestones(IEclipsePreferences preferences) { 541 int eclipseVersion = preferences.getInt(IPerformancesConstants.PRE_ECLIPSE_VERSION, IPerformancesConstants.DEFAULT_ECLIPSE_VERSION); 542 String prefix = IPerformancesConstants.PRE_MILESTONE_BUILDS + "." + eclipseVersion; 543 int index = 0; 544 String milestone = preferences.get(prefix + index, null); 545 String[] milestones = new String[20]; 546 while (milestone != null) { 547 milestones[index] = milestone; 548 index++; 549 milestone = preferences.get(prefix + index, null); 550 } 551 int length = milestones.length; 552 if (index < length) { 553 System.arraycopy(milestones, 0, milestones = new String[index], 0, index); 554 } 555 MILESTONES = milestones; 556 } 557 } 558