Home | History | Annotate | Download | only in utils
      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