Home | History | Annotate | Download | only in db
      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.db;
     12 
     13 import java.io.BufferedInputStream;
     14 import java.io.BufferedOutputStream;
     15 import java.io.DataInputStream;
     16 import java.io.DataOutputStream;
     17 import java.io.File;
     18 import java.io.FileInputStream;
     19 import java.io.FileNotFoundException;
     20 import java.io.FileOutputStream;
     21 import java.io.IOException;
     22 import java.io.PrintStream;
     23 import java.util.*;
     24 
     25 import org.eclipse.core.runtime.IProgressMonitor;
     26 import org.eclipse.core.runtime.OperationCanceledException;
     27 import org.eclipse.core.runtime.SubMonitor;
     28 import org.eclipse.test.internal.performance.results.utils.Util;
     29 
     30 
     31 /**
     32  * Root class to handle performance results.
     33  *
     34  * Usually performance results are built for a current build vs. a baseline build.
     35  *
     36  * This class allow to read all data from releng performance database for given
     37  * configurations and scenario pattern.
     38  *
     39  * Then it provides easy and speedy access to all stored results.
     40  */
     41 public class PerformanceResults extends AbstractResults {
     42 
     43 	String[] allBuildNames = null;
     44 	Map allScenarios;
     45 	String lastBuildName; // Name of the last used build
     46 	String baselineName; // Name of the baseline build used for comparison
     47 	String baselinePrefix;
     48 	private String scenarioPattern = "%"; //$NON-NLS-1$
     49 	private String[] components;
     50 	String[] configNames, sortedConfigNames;
     51 	String[] configDescriptions, sortedConfigDescriptions;
     52 	private String configPattern;
     53 
     54 	boolean dbRequired;
     55 	boolean needToUpdateLocalFile;
     56 
     57 	/*
     58 	 * Local class helping to guess remaining time while reading results from DB
     59 	 */
     60 	class RemainingTimeGuess {
     61 		int all, count;
     62 		long start;
     63 		double remaining;
     64 		RemainingTimeGuess(int all, long start) {
     65 			this.all = all;
     66 			this.start = start;
     67 		}
     68 		String display() {
     69 			StringBuffer buffer = new StringBuffer(" [elapsed: "); //$NON-NLS-1$
     70 			long elapsed = getElapsed();
     71 			buffer.append(Util.timeChrono(elapsed));
     72 			if (this.count > 0) {
     73 				buffer.append(" | left: "); //$NON-NLS-1$
     74 				long remainingTime = getRemainingTime(elapsed);
     75 				buffer.append(Util.timeChrono(remainingTime));
     76 				buffer.append(" | end: "); //$NON-NLS-1$
     77 				buffer.append(Util.timeEnd(remainingTime));
     78 			}
     79 			buffer.append(']');
     80 			return buffer.toString();
     81 		}
     82 		private long getRemainingTime(long elapsed) {
     83 			return (long) ((((double)elapsed) / this.count) * (this.all - this.count));
     84 	    }
     85 		private long getElapsed() {
     86 			return System.currentTimeMillis() - this.start;
     87 	    }
     88 	}
     89 
     90 
     91 	// Failure threshold
     92 	public static final int DEFAULT_FAILURE_THRESHOLD = 10;
     93 	int failure_threshold = DEFAULT_FAILURE_THRESHOLD;
     94 
     95 public PerformanceResults(PrintStream stream) {
     96 	super(null, null);
     97 	this.printStream = stream;
     98 	this.dbRequired = false;
     99 	setDefaults();
    100 }
    101 
    102 public PerformanceResults(String name, String baseline, String baselinePrefix, PrintStream stream) {
    103 	super(null, name);
    104 	this.baselineName = baseline;
    105 	this.baselinePrefix = baselinePrefix;
    106 	this.printStream = stream;
    107 	this.dbRequired = true;
    108 	setDefaults();
    109 }
    110 
    111 /**
    112  * Returns the list of all builds currently read.
    113  *
    114  * @return The names list of all currently known builds
    115  */
    116 public String[] getAllBuildNames() {
    117 	if (this.allBuildNames == null) {
    118 		setAllBuildNames();
    119 	}
    120 	return this.allBuildNames;
    121 }
    122 
    123 /**
    124  * Returns the name of the baseline used for extracted results
    125  *
    126  * @return The build name of the baseline of <code>null</code>
    127  * 	if no specific baseline is used for the extracted results.
    128  */
    129 public String getBaselineName() {
    130 	return this.baselineName;
    131 }
    132 
    133 /*
    134  * Get the baseline prefix (computed from #baselineName).
    135  */
    136 String getBaselinePrefix() {
    137 	return this.baselinePrefix;
    138 }
    139 
    140 /*
    141  * Get the build date (see #getBuildDate(String, String)).
    142  */
    143 public String getBuildDate() {
    144 	String buildName = getName();
    145 	if (buildName == null) return ""; //$NON-NLS-1$
    146 	return Util.getBuildDate(getName(), getBaselinePrefix());
    147 }
    148 
    149 /**
    150  * Return the list of components concerned by performance results.
    151  *
    152  * @return The list of the components
    153  */
    154 public String[] getComponents() {
    155 	return this.components;
    156 }
    157 
    158 /**
    159  * Get the scenarios of a given component.
    160  *
    161  * @param componentName The component name. Should not be <code>null</code>
    162  * @return A list of {@link ScenarioResults scenario results}
    163  */
    164 public List getComponentScenarios(String componentName) {
    165 	ComponentResults componentResults = (ComponentResults) getResults(componentName);
    166 	if (componentResults == null) return null;
    167 	return Collections.unmodifiableList(componentResults.children);
    168 }
    169 
    170 /**
    171  * Get the scenarios which have a summary for a given component.
    172  *
    173  * @param componentName The component name
    174  * @param config Configuration name
    175  * @return A list of {@link ScenarioResults scenario results} which have a summary
    176  */
    177 public List getComponentSummaryScenarios(String componentName, String config) {
    178 	if (componentName == null) {
    179 		int size = size();
    180 		List scenarios = new ArrayList();
    181 		for (int i=0; i< size; i++) {
    182 			ComponentResults componentResults = (ComponentResults) this.children.get(i);
    183 			scenarios.addAll(componentResults.getSummaryScenarios(true, config));
    184 		}
    185 		return scenarios;
    186 	}
    187 	ComponentResults componentResults = (ComponentResults) getResults(componentName);
    188 	return componentResults.getSummaryScenarios(false, config);
    189 }
    190 
    191 /**
    192  * Return the configuration boxes considered for this performance results
    193  * sorted or not depending on the given flag.
    194  *
    195  * @param sort Indicates whether the list must be sorted or not.
    196  * 	The order is defined by the configuration names, not by the box names
    197  * @return The list of configuration boxes sorted by configuration names
    198  */
    199 public String[] getConfigBoxes(boolean sort) {
    200 	return sort ? this.sortedConfigDescriptions : this.configDescriptions;
    201 }
    202 
    203 /**
    204  * Return the configuration names considered for this performance results
    205  * sorted or not depending on the given flag.
    206  *
    207  * @param sort Indicates whether the list must be sorted or not
    208  * @return The list of configuration names
    209  */
    210 public String[] getConfigNames(boolean sort) {
    211 	return sort ?this.sortedConfigNames : this.configNames;
    212 }
    213 
    214 /*
    215  * Compute a SQL pattern from all stored configuration names.
    216  * For example 'eclipseperflnx1', 'eclipseperflnx2' and 'eclipseperflnx3'
    217  * will return 'eclipseperflnx_'.
    218  */
    219 String getConfigurationsPattern() {
    220 	if (this.configPattern == null) {
    221 		int length = this.sortedConfigNames == null ? 0 : this.sortedConfigNames.length;
    222 		if (length == 0) return null;
    223 		this.configPattern = this.sortedConfigNames[0];
    224 		int refLength = this.configPattern.length();
    225 		for (int i=1; i<length; i++) {
    226 			String config = this.sortedConfigNames[i];
    227 			StringBuffer newConfig = null;
    228 			if (refLength != config.length()) return null; // strings have not the same length => cannot find a pattern
    229 			for (int j=0; j<refLength; j++) {
    230 				char c = this.configPattern.charAt(j);
    231 				if (config.charAt(j) != c) {
    232 					if (newConfig == null) {
    233 						newConfig = new StringBuffer(refLength);
    234 						if (j == 0) return null; // first char is already different => cannot find a pattern
    235 						newConfig.append(this.configPattern.substring(0, j));
    236 					}
    237 					newConfig.append('_');
    238 				} else if (newConfig != null) {
    239 					newConfig.append(c);
    240 				}
    241 			}
    242 			if (newConfig != null) {
    243 				this.configPattern = newConfig.toString();
    244 			}
    245 		}
    246 	}
    247 	return this.configPattern;
    248 }
    249 
    250 /**
    251  * Return the name of the last build name except baselines.
    252  *
    253  * @return the name of the last build
    254  */
    255 public String getLastBuildName() {
    256 	return getLastBuildName(1/*all except baselines*/);
    257 }
    258 /**
    259  * Return the name of the last build name
    260  *
    261  * @param kind Decide what kind of build is taken into account
    262  * 	0: all kind of build
    263  * 	1: all except baseline builds
    264  * 	2: all except baseline and nightly builds
    265  * 	3: only integration builds
    266  * @return the name of the last build of the selected kind
    267  */
    268 public String getLastBuildName(int kind) {
    269 	if (this.name == null) {
    270 		getAllBuildNames(); // init build names if necessary
    271 		int idx = this.allBuildNames.length-1;
    272 		this.name = this.allBuildNames[idx];
    273 		if (kind > 0) {
    274 			loop: while (idx-- >= 0) {
    275 				switch (this.name.charAt(0)) {
    276 					case 'N':
    277 						if (kind < 2) break loop;
    278 						break;
    279 					case 'M':
    280 						if (kind < 3) break loop;
    281 						break;
    282 					case 'I':
    283 						if (kind < 4) break loop;
    284 						break;
    285 				}
    286 				this.name = this.allBuildNames[idx];
    287 			}
    288 		}
    289 	}
    290 	return this.name;
    291 }
    292 
    293 public String getName() {
    294 	if (this.name == null) {
    295 		setAllBuildNames();
    296 	}
    297 	return this.name;
    298 }
    299 
    300 /*
    301  * (non-Javadoc)
    302  * @see org.eclipse.test.internal.performance.results.AbstractResults#getPerformance()
    303  */
    304 PerformanceResults getPerformance() {
    305 	return this;
    306 }
    307 
    308 /**
    309  * Get the results of a given scenario.
    310  *
    311  * @param scenarioName The scenario name
    312  * @return The {@link ScenarioResults scenario results}
    313  */
    314 public ScenarioResults getScenarioResults(String scenarioName) {
    315 	ComponentResults componentResults = (ComponentResults) getResults(DB_Results.getComponentNameFromScenario(scenarioName));
    316 	return componentResults == null ? null : (ScenarioResults) componentResults.getResults(scenarioName);
    317 }
    318 
    319 /*
    320  * Init configurations from performance results database.
    321  */
    322 private void initConfigs() {
    323 	// create config names
    324 	this.configNames = DB_Results.getConfigs();
    325 	this.configDescriptions = DB_Results.getConfigDescriptions();
    326 	int length = this.configNames.length;
    327 	this.sortedConfigNames = new String[length];
    328 	for (int i = 0; i < length; i++) {
    329 	    this.sortedConfigNames[i] = this.configNames[i];
    330 	}
    331 
    332 	// Sort the config names
    333 	Arrays.sort(this.sortedConfigNames);
    334 	this.sortedConfigDescriptions = new String[length];
    335 	for (int i=0; i<length; i++) {
    336 		for (int j=0; j<length; j++) {
    337 			if (this.sortedConfigNames[i] == this.configNames[j]) { // == is intentional!
    338 				this.sortedConfigDescriptions[i] = this.configDescriptions[j];
    339 				break;
    340 			}
    341 		}
    342 	}
    343 }
    344 
    345 /*
    346  * Read or update data for a build from a directory supposed to have local files.
    347  */
    348 private String[] read(boolean local, String buildName, String[][] configs, boolean force, File dataDir, String taskName, SubMonitor subMonitor) {
    349 	if (local && dataDir == null) {
    350 		throw new IllegalArgumentException("Must specify a directory to read local files!"); //$NON-NLS-1$
    351 	}
    352 	subMonitor.setWorkRemaining(100);
    353 
    354 	// Update info
    355 	long start = System.currentTimeMillis();
    356 	int allScenariosSize;
    357 	if (DB_Results.DB_CONNECTION) {
    358 		try {
    359 			// Read all scenarios
    360 			allScenariosSize = readScenarios(buildName, subMonitor.newChild(10)) ;
    361 			if (allScenariosSize < 0) {
    362 				return null;
    363 			}
    364 
    365 			// Read all builds
    366 			DB_Results.queryAllVariations(getConfigurationsPattern());
    367 
    368 			// Refresh configs
    369 			if (configs == null) {
    370 				initConfigs();
    371 			} else {
    372 				setConfigInfo(configs);
    373 			}
    374 		} catch (OperationCanceledException e) {
    375 			return null;
    376 		}
    377 	} else {
    378 		if (this.allScenarios == null) return null;
    379 		allScenariosSize = this.allScenarios.size();
    380 		if (configs != null) {
    381 			setConfigInfo(configs);
    382 		}
    383 	}
    384 
    385 	// Create corresponding children
    386 	int componentsLength = this.components.length;
    387 	subMonitor.setWorkRemaining(componentsLength);
    388 	RemainingTimeGuess timeGuess = null;
    389 	for (int i=0; i<componentsLength; i++) {
    390 		String componentName = this.components[i];
    391 		List scenarios = this.allScenarios == null ? null : (List) this.allScenarios.get(componentName);
    392 
    393 		// Manage monitor
    394 		int percentage = (int) ((((double)(i+1)) / (componentsLength+1)) * 100);
    395 		StringBuffer tnBuffer= taskName==null ? new StringBuffer() : new StringBuffer(taskName);
    396 		tnBuffer.append(" ("); //$NON-NLS-1$
    397 		if (buildName != null) {
    398 			tnBuffer.append(buildName).append(": "); //$NON-NLS-1$
    399 		}
    400 		tnBuffer.append(percentage).append("%)"); //$NON-NLS-1$
    401 		subMonitor.setTaskName(tnBuffer.toString());
    402 		StringBuffer subTaskBuffer = new StringBuffer("Component "); //$NON-NLS-1$
    403 		subTaskBuffer.append(componentName);
    404 		subTaskBuffer.append("..."); //$NON-NLS-1$
    405 		subMonitor.subTask(subTaskBuffer.toString());
    406 
    407 		// Get component results
    408 		if (scenarios == null && !local) continue;
    409 		ComponentResults componentResults;
    410 		if (local || (buildName == null && force)) {
    411 			componentResults = new ComponentResults(this, componentName);
    412 			addChild(componentResults, true);
    413 		} else {
    414 			componentResults = (ComponentResults) getResults(componentName);
    415 			if (componentResults == null) {
    416 				componentResults = new ComponentResults(this, componentName);
    417 				addChild(componentResults, true);
    418 			}
    419 		}
    420 
    421 		// Read the component results
    422 		if (local) {
    423 			try {
    424 				componentResults.readLocalFile(dataDir, scenarios);
    425 			}
    426 			catch (FileNotFoundException ex) {
    427 				return null;
    428 			}
    429 			subMonitor.worked(1);
    430 		} else {
    431 			if (timeGuess == null) {
    432 				timeGuess = new RemainingTimeGuess(1+componentsLength+allScenariosSize, start);
    433 			}
    434 			componentResults.updateBuild(buildName, scenarios, force, dataDir, subMonitor.newChild(1), timeGuess);
    435 		}
    436 		if (subMonitor.isCanceled()) return null;
    437 	}
    438 
    439 	// Update names
    440 	setAllBuildNames();
    441 	writeData(dataDir);
    442 
    443 	// Print time
    444 	printGlobalTime(start);
    445 
    446 	return this.allBuildNames;
    447 }
    448 
    449 /**
    450  * Read all data from performance database for the given configurations
    451  * and scenario pattern.
    452  *
    453  * This method is typically called when generated performance results
    454  * from a non-UI application.
    455  *
    456  * @param buildName The name of the build
    457  * @param configs All configurations to extract results. If <code>null</code>,
    458  * 	then all known configurations ({@link DB_Results#getConfigs()})  are read.
    459  * @param pattern The pattern of the concerned scenarios
    460  * @param dataDir The directory where data will be read/stored locally.
    461  * 	If <code>null</code>, then database will be read instead and no storage
    462  * 	will be performed
    463  * @param threshold The failure percentage threshold over which a build result
    464  * 	value compared to the baseline is considered as failing.
    465  * @param monitor The progress monitor
    466  *
    467  * @return All known build names
    468  */
    469 public String[] readAll(String buildName, String[][] configs, String pattern, File dataDir, int threshold, IProgressMonitor monitor) {
    470 
    471 	// Init
    472 	this.scenarioPattern = pattern == null ? "%" : pattern; //$NON-NLS-1$
    473 	this.failure_threshold = threshold;
    474 	SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
    475 
    476 	// Set default names
    477 	setDefaults();
    478 
    479 	// Read local data files first
    480 	reset(dataDir);
    481 	String[] names = read(true, null, configs, true, dataDir, null, subMonitor.newChild(100));
    482 	if (names==null) {
    483 		// if one local files is missing then force a full DB read!
    484 		// TODO moderate this to force the DB read only for the missing file...
    485 		return read(false, null, configs, true, dataDir, null, subMonitor.newChild(900));
    486 	}
    487 
    488 	// Search build name in read data
    489 	boolean buildMissing = true;
    490 	if (buildName != null) {
    491 		this.name = buildName;
    492 		buildMissing = Arrays.binarySearch(names, buildName, Util.BUILD_DATE_COMPARATOR) < 0;
    493 	}
    494 
    495 	// Look for missing builds
    496 	if (buildMissing) {
    497 		String[] builds = DB_Results.getBuilds();
    498 		Arrays.sort(builds, Util.BUILD_DATE_COMPARATOR);
    499 		for (int i=builds.length-1; i>=0; i--) {
    500 			if (Arrays.binarySearch(names, builds[i], Util.BUILD_DATE_COMPARATOR) >= 0) {
    501 				break;
    502 			}
    503 			read(false, builds[i], configs, true, dataDir, null, subMonitor.newChild(900));
    504 		}
    505 	}
    506 	return this.allBuildNames;
    507 }
    508 
    509 /**
    510  * Read all data from performance database for the given configurations
    511  * and scenario pattern.
    512  *
    513  * Note that calling this method flush all previous read data.
    514  *
    515  * @param dataDir The directory where local files are located
    516  * @param monitor  The progress monitor
    517  */
    518 public void readLocal(File dataDir, IProgressMonitor monitor) {
    519 
    520 	// Print title
    521 	String taskName = "Read local performance results"; //$NON-NLS-1$
    522 	println(taskName);
    523 
    524 	// Create monitor
    525 	SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
    526 	subMonitor.setTaskName(taskName);
    527 
    528 	// Read
    529 	reset(dataDir);
    530 	read(true, null, null, true, dataDir, taskName, subMonitor);
    531 }
    532 
    533 void readLocalFile(File dir) {
    534 	if (!dir.exists()) return;
    535 	File dataFile = new File(dir, "performances.dat");	//$NON-NLS-1$
    536 	if (!dataFile.exists()) return;
    537 	DataInputStream stream = null;
    538 	try {
    539 		// Read local file info
    540 		stream = new DataInputStream(new BufferedInputStream(new FileInputStream(dataFile)));
    541 
    542 		// Read build info
    543 		String str = stream.readUTF();
    544 		this.needToUpdateLocalFile = this.name == null || Util.getBuildDate(this.name).compareTo(Util.getBuildDate(str)) > 0;
    545 		if (this.name != null && Util.getBuildDate(this.name).compareTo(Util.getBuildDate(str)) >= 0) {
    546 			return;
    547 		}
    548 		println(" - read performance results local files info: "); //$NON-NLS-1$
    549 		println("		+ name : "+str);
    550 		this.name = str == ""  ? null : str;
    551 		str = stream.readUTF();
    552 		println("		+ baseline : "+str);
    553 		if (this.baselineName == null) {
    554 			this.baselineName = str == "" ? null : str;
    555 		}
    556 		str = stream.readUTF();
    557 		println("		+ baseline prefix: "+str);
    558 		this.baselinePrefix = str == "" ? null : str;
    559 
    560 		// Write configs info
    561 		int length = stream.readInt();
    562 		println("		+ "+length+" configs");
    563 		this.configNames = new String[length];
    564 		this.sortedConfigNames = new String[length];
    565 		this.configDescriptions = new String[length];
    566 		this.sortedConfigDescriptions = new String[length];
    567 		for (int i = 0; i < length; i++) {
    568 			this.configNames[i] = stream.readUTF();
    569 			this.sortedConfigNames[i] = this.configNames[i];
    570 			this.configDescriptions[i] = stream.readUTF();
    571 			this.sortedConfigDescriptions[i] = this.configDescriptions[i];
    572 		}
    573 		DB_Results.setConfigs(this.configNames);
    574 		DB_Results.setConfigDescriptions(this.configDescriptions);
    575 
    576 		// Write builds info
    577 		length = stream.readInt();
    578 		println("		+ "+length+" builds");
    579 		this.allBuildNames = new String[length];
    580 		for (int i = 0; i < length; i++) {
    581 			this.allBuildNames[i] = stream.readUTF();
    582 		}
    583 
    584 		// Write scenarios info
    585 		length = stream.readInt();
    586 		println("		+ "+length+" components");
    587 		this.components = new String[length];
    588 		this.allScenarios = new HashMap();
    589 		for (int i = 0; i < length; i++) {
    590 			this.components[i] = stream.readUTF();
    591 			int size = stream.readInt();
    592 			List scenarios = new ArrayList(size);
    593 			for (int j=0; j<size; j++) {
    594 				scenarios.add(new ScenarioResults(stream.readInt(), stream.readUTF(), stream.readUTF()));
    595 			}
    596 			this.allScenarios.put(this.components[i], scenarios);
    597 		}
    598 		println("	=> read from file "+dataFile); //$NON-NLS-1$
    599 	} catch (IOException ioe) {
    600 		println("	!!! "+dataFile+" should be deleted as it contained invalid data !!!"); //$NON-NLS-1$ //$NON-NLS-2$
    601 	} finally {
    602 		try {
    603 	        stream.close();
    604         } catch (IOException e) {
    605 	        // nothing else to do!
    606         }
    607 	}
    608 }
    609 
    610 private int readScenarios(String buildName, SubMonitor subMonitor) throws OperationCanceledException {
    611 	subMonitor.setWorkRemaining(10);
    612 	long start = System.currentTimeMillis();
    613 	String titleSuffix;
    614 	if (buildName == null) {
    615 		titleSuffix = "all database scenarios..."; //$NON-NLS-1$
    616 	} else {
    617 		titleSuffix = "all database scenarios for "+buildName+" build..."; //$NON-NLS-1$ //$NON-NLS-2$
    618 	}
    619 	print("	+ get "+titleSuffix); //$NON-NLS-1$
    620 	subMonitor.subTask("Get "+titleSuffix); //$NON-NLS-1$
    621 	this.allScenarios = DB_Results.queryAllScenarios(this.scenarioPattern, buildName);
    622 	if (this.allScenarios == null) return -1;
    623 	int allScenariosSize = 0;
    624 	List componentsSet = new ArrayList(this.allScenarios.keySet());
    625 	Collections.sort(componentsSet);
    626 	int componentsSize = componentsSet.size();
    627 	componentsSet.toArray(this.components = new String[componentsSize]);
    628 	for (int i=0; i<componentsSize; i++) {
    629 		String componentName = this.components[i];
    630 		List scenarios = (List) this.allScenarios.get(componentName);
    631 		allScenariosSize += scenarios.size();
    632 	}
    633 	println(" -> "+allScenariosSize+" found in "+(System.currentTimeMillis()-start)+"ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
    634 	subMonitor.worked(10);
    635 	if (subMonitor.isCanceled()) throw new OperationCanceledException();
    636 	return allScenariosSize;
    637 }
    638 
    639 void reset(File dataDir) {
    640 	this.allBuildNames = null;
    641 	this.children = new ArrayList();
    642 //	this.name = null;
    643 	this.components = null;
    644 	this.allScenarios = null;
    645 	readLocalFile(dataDir);
    646 }
    647 
    648 private void setAllBuildNames() {
    649 	SortedSet builds = new TreeSet(Util.BUILD_DATE_COMPARATOR);
    650 	int size = size();
    651 	if (size == 0) return;
    652 	for (int i=0; i<size; i++) {
    653 		ComponentResults componentResults = (ComponentResults) this.children.get(i);
    654 		Set names = componentResults.getAllBuildNames();
    655 		builds.addAll(names);
    656 	}
    657 	int buildsSize = builds.size();
    658 	this.allBuildNames = new String[buildsSize];
    659 	if (buildsSize > 0) {
    660 		int n = 0;
    661 		Iterator buildNames = builds.iterator();
    662 		while (buildNames.hasNext()) {
    663 			String buildName = (String) buildNames.next();
    664 			if (this.lastBuildName == null || Util.getBuildDate(buildName).compareTo(Util.getBuildDate(this.lastBuildName)) <= 0) {
    665 				this.allBuildNames[n++] = buildName;
    666 			}
    667 		}
    668 		if (n < buildsSize) {
    669 			System.arraycopy(this.allBuildNames, 0, this.allBuildNames = new String[n], 0, n);
    670 		}
    671 		int idx = n-1;
    672 		String lastBuild = this.allBuildNames[idx--];
    673 		while (lastBuild.startsWith(DB_Results.getDbBaselinePrefix())) {
    674 			lastBuild = this.allBuildNames[idx--];
    675 		}
    676 		this.needToUpdateLocalFile = this.name == null || Util.getBuildDate(lastBuild).compareTo(Util.getBuildDate(this.name)) > 0;
    677 		this.name = lastBuild;
    678 		if (this.baselineName != null) {
    679 			String lastBuildDate = Util.getBuildDate(lastBuild);
    680 			if (Util.getBuildDate(this.baselineName).compareTo(lastBuildDate) > 0) {
    681 				this.baselineName = DB_Results.getLastBaselineBuild(lastBuildDate);
    682 			}
    683 		}
    684 	}
    685 }
    686 
    687 private void setConfigInfo(String[][] configs) {
    688 	if (configs == null) return;
    689 
    690 	// Store config information
    691 	int length = configs.length;
    692 	this.configNames = new String[length];
    693 	this.sortedConfigNames = new String[length];
    694 	this.configDescriptions = new String[length];
    695 	for (int i=0; i<length; i++) {
    696 		this.configNames[i] = this.sortedConfigNames[i] = configs[i][0];
    697 		this.configDescriptions[i] = configs[i][1];
    698 	}
    699 
    700 	// Sort the config names
    701 	Arrays.sort(this.sortedConfigNames);
    702 	length = this.sortedConfigNames.length;
    703 	this.sortedConfigDescriptions = new String[length];
    704 	for (int i=0; i<length; i++) {
    705 		for (int j=0; j<length; j++) {
    706 			if (this.sortedConfigNames[i] == this.configNames[j]) { // == is intentional!
    707 				this.sortedConfigDescriptions[i] = this.configDescriptions[j];
    708 				break;
    709 			}
    710 		}
    711 	}
    712 }
    713 
    714 
    715 /**
    716  * Set the name of the baseline used for extracted results
    717  *
    718  * @param buildName The name of the baseline build
    719  */
    720 public void setBaselineName(String buildName) {
    721 	this.baselineName = buildName;
    722 }
    723 
    724 private void setDefaults() {
    725 
    726 	// Set builds if none
    727 	if (size() == 0 && DB_Results.DB_CONNECTION) {
    728 		this.allBuildNames = DB_Results.getBuilds();
    729 		this.components = DB_Results.getComponents();
    730 		initConfigs();
    731 	}
    732 
    733 	// Set name if null
    734 	if (this.name == null) {
    735 		setAllBuildNames();
    736 		if (this.name == null) { // does not know any build
    737 			this.name = DB_Results.getLastCurrentBuild();
    738 			if (this.dbRequired) {
    739 				if (this.name == null) {
    740 					throw new RuntimeException("Cannot find any current build!"); //$NON-NLS-1$
    741 				}
    742 				this.allBuildNames = DB_Results.getBuilds();
    743 				this.components = DB_Results.getComponents();
    744 				initConfigs();
    745 			}
    746 			if (this.printStream != null) {
    747 				this.printStream.println("	+ no build specified => use last one: "+this.name); //$NON-NLS-1$
    748 			}
    749 		}
    750 	}
    751 
    752 	// Init baseline name if not set
    753 	if (this.baselineName == null && getName() != null) {
    754 		String buildDate = Util.getBuildDate(getName());
    755 		this.baselineName = DB_Results.getLastBaselineBuild(buildDate);
    756 		if (this.baselineName == null && this.dbRequired) {
    757 			throw new RuntimeException("Cannot find any baseline to refer!"); //$NON-NLS-1$
    758 		}
    759 		if (this.printStream != null) {
    760 			this.printStream.println("	+ no baseline specified => use last one: "+this.baselineName); //$NON-NLS-1$
    761 		}
    762 	}
    763 
    764 	// Init baseline prefix if not set
    765 	if (this.baselinePrefix == null && this.baselineName != null) {
    766 		// Assume that baseline name format is *always* x.y_yyyyMMddhhmm_yyyyMMddhhmm
    767 		int index = this.baselineName.lastIndexOf('_');
    768 		if (index > 0) {
    769 			this.baselinePrefix = this.baselineName.substring(0, index);
    770 		} else {
    771 			this.baselinePrefix = DB_Results.getDbBaselinePrefix();
    772 		}
    773 	}
    774 
    775 	// Set scenario pattern default
    776 	if (this.scenarioPattern == null) {
    777 		this.scenarioPattern = "%"; //$NON-NLS-1$
    778 	}
    779 
    780 	// Flush print stream
    781 	if (this.printStream != null) {
    782 		this.printStream.println();
    783 		this.printStream.flush();
    784 	}
    785 }
    786 
    787 public void setLastBuildName(String lastBuildName) {
    788 	this.lastBuildName = lastBuildName;
    789 //	if (lastBuildName == null) {
    790 //		int idx = this.allBuildNames.length-1;
    791 //		String lastBuild = this.allBuildNames[idx--];
    792 //		while (this.name.startsWith(DB_Results.getDbBaselinePrefix())) {
    793 //			lastBuild = this.allBuildNames[idx--];
    794 //		}
    795 //		this.name = lastBuild;
    796 //	} else {
    797 //		this.name = lastBuildName;
    798 //	}
    799 }
    800 
    801 /**
    802  * Update a given build information with database contents.
    803  *
    804  * @param builds The builds to read new data
    805  * @param force Force the update from the database, even if the build is
    806  * 	already known.
    807  * @param dataDir The directory where data should be stored locally if necessary.
    808  * 	If <code>null</code>, then information changes won't be persisted.
    809  * @param monitor The progress monitor
    810  * @return All known builds
    811  */
    812 public String[] updateBuilds(String[] builds, boolean force, File dataDir, IProgressMonitor monitor) {
    813 
    814 	// Print title
    815 	StringBuffer buffer = new StringBuffer("Update data for "); //$NON-NLS-1$
    816 	int length = builds == null ? 0 : builds.length;
    817 	switch (length) {
    818 		case 0:
    819 			buffer.append("all builds"); //$NON-NLS-1$
    820 			reset(dataDir);
    821 			break;
    822 		case 1:
    823 			buffer.append("one build"); //$NON-NLS-1$
    824 			break;
    825 		default:
    826 			buffer.append("several builds"); //$NON-NLS-1$
    827 			break;
    828 	}
    829 	String taskName = buffer.toString();
    830 	println(buffer);
    831 
    832 	// Create sub-monitor
    833 	SubMonitor subMonitor = SubMonitor.convert(monitor, 1000*length);
    834 	subMonitor.setTaskName(taskName);
    835 
    836 	// Read
    837 	for (int i=0; i<length;  i++) {
    838 		read(false, builds[i], null, force, dataDir, taskName, subMonitor.newChild(1000));
    839 	}
    840 
    841 	// Return new builds list
    842 	return this.allBuildNames;
    843 }
    844 
    845 /**
    846  * Update a given build information with database contents.
    847  *
    848  * @param buildName The build name to read new data
    849  * @param force Force the update from the database, even if the build is
    850  * 	already known.
    851  * @param dataDir The directory where data should be stored locally if necessary.
    852  * 	If <code>null</code>, then information changes won't be persisted.
    853  * @param monitor The progress monitor
    854  * @return All known builds
    855  */
    856 public String[] updateBuild(String buildName, boolean force, File dataDir, IProgressMonitor monitor) {
    857 
    858 	// Print title
    859 	StringBuffer buffer = new StringBuffer("Update data for "); //$NON-NLS-1$
    860 	if (buildName == null) {
    861 		buffer.append("all builds"); //$NON-NLS-1$
    862 		reset(dataDir);
    863 	} else {
    864 		buffer.append("one build"); //$NON-NLS-1$
    865 	}
    866 	String taskName = buffer.toString();
    867 	println(buffer);
    868 
    869 	// Create sub-monitor
    870 	SubMonitor subMonitor = SubMonitor.convert(monitor, 1000);
    871 	subMonitor.setTaskName(taskName);
    872 
    873 	// Read
    874 	read(false, buildName, null, force, dataDir, taskName, subMonitor);
    875 
    876 	// Refresh name
    877 	if (buildName != null && !buildName.startsWith(DB_Results.getDbBaselinePrefix())) {
    878 		this.name = buildName;
    879 	}
    880 
    881 	// Return new list all build names
    882 	return this.allBuildNames;
    883 }
    884 
    885 /*
    886  * Write general information.
    887  */
    888 void writeData(File dir) {
    889 	if (!DB_Results.DB_CONNECTION) {
    890 		// Only write new local file if there's a database connection
    891 		// otherwise contents may not be complete...
    892 		return;
    893 	}
    894 	if (dir ==null || (!dir.exists() && !dir.mkdirs())) {
    895 		System.err.println("can't create directory " + dir); //$NON-NLS-1$
    896 		return;
    897 	}
    898 	File dataFile = new File(dir, "performances.dat"); //$NON-NLS-1$
    899 	if (dataFile.exists()) {
    900 		if (!this.needToUpdateLocalFile) {
    901 			return;
    902 		}
    903 		dataFile.delete();
    904 	}
    905 	try {
    906 		DataOutputStream stream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile)));
    907 
    908 		// Write build info
    909 		stream.writeUTF(this.name == null ? DB_Results.getLastCurrentBuild() : this.name);
    910 		stream.writeUTF(this.baselineName == null ? DB_Results.getLastBaselineBuild(null) : this.baselineName);
    911 		stream.writeUTF(this.baselinePrefix == null ? "" : this.baselinePrefix);
    912 
    913 		// Write configs info
    914 		int length = this.sortedConfigNames.length;
    915 		stream.writeInt(length);
    916 		for (int i = 0; i < length; i++) {
    917 			stream.writeUTF(this.sortedConfigNames[i]);
    918 			stream.writeUTF(this.sortedConfigDescriptions[i]);
    919 		}
    920 
    921 		// Write builds info
    922 		String[] builds = this.allBuildNames == null ? DB_Results.getBuilds() : this.allBuildNames;
    923 		length = builds.length;
    924 		stream.writeInt(length);
    925 		for (int i = 0; i < length; i++) {
    926 			stream.writeUTF(builds[i]);
    927 		}
    928 
    929 		// Write scenarios info
    930 		length = this.components.length;
    931 		stream.writeInt(length);
    932 		for (int i = 0; i < length; i++) {
    933 			stream.writeUTF(this.components[i]);
    934 			List scenarios = (List) this.allScenarios.get(this.components[i]);
    935 			int size = scenarios.size();
    936 			stream.writeInt(size);
    937 			for (int j=0; j<size; j++) {
    938 				final ScenarioResults scenarioResults = (ScenarioResults)scenarios.get(j);
    939 				stream.writeInt(scenarioResults.getId());
    940 				stream.writeUTF(scenarioResults.getName());
    941 				stream.writeUTF(scenarioResults.getLabel());
    942 			}
    943 		}
    944 
    945 		// Close
    946 		stream.close();
    947 		println("	=> performance results general data  written in file " + dataFile); //$NON-NLS-1$
    948 	} catch (FileNotFoundException e) {
    949 		System.err.println("can't create output file" + dataFile); //$NON-NLS-1$
    950 	} catch (IOException e) {
    951 		e.printStackTrace();
    952 	}
    953 }
    954 
    955 }
    956