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 *******************************************************************************/
11package org.eclipse.test.internal.performance.results.db;
12
13import java.io.DataInputStream;
14import java.io.DataOutputStream;
15import java.io.IOException;
16import java.util.Arrays;
17import java.util.Comparator;
18import java.util.HashSet;
19import java.util.List;
20import java.util.Set;
21import java.util.StringTokenizer;
22
23import org.eclipse.test.internal.performance.results.utils.Util;
24
25
26/**
27 * Class to handle performance results of a component's scenario
28 * (for example 'org.eclipse.jdt.core.FullSourceWorkspaceSearchTest#searchAllTypeNames()').
29 *
30 * It gives access to results for each configuration run on this scenario.
31 *
32 * @see ConfigResults
33 */
34public class ScenarioResults extends AbstractResults {
35	String fileName;
36	String label;
37	String shortName;
38
39public ScenarioResults(int id, String name, String shortName) {
40	super(null, id);
41	this.name = name;
42	this.label = shortName;
43}
44
45/*
46 * Complete results with additional database information.
47 */
48void completeResults(String lastBuildName) {
49	String[] builds = DB_Results.getBuilds();
50	class BuildDateComparator implements Comparator {
51		public int compare(Object o1, Object o2) {
52	        String s1 = (String) o1;
53	        String s2 = (String) o2;
54	        return Util.getBuildDate(s1).compareTo(Util.getBuildDate(s2));
55	    }
56	}
57	BuildDateComparator comparator = new BuildDateComparator();
58	Arrays.sort(builds, comparator);
59	int idx = Arrays.binarySearch(builds, lastBuildName, comparator);
60	if (idx < 0) {
61		builds = null;
62	} else {
63		int size = builds.length - ++idx;
64		System.arraycopy(builds, idx, builds = new String[size], 0, size);
65	}
66//	String[] builds = null;
67	int size = size();
68	for (int i=0; i<size; i++) {
69		ConfigResults configResults = (ConfigResults) this.children.get(i);
70		configResults.completeResults(builds);
71	}
72}
73
74/**
75 * Returns the first configuration baseline build name.
76 *
77 * @return The name of the baseline build
78 * @see ConfigResults#getBaselineBuildName()
79 */
80public String getBaselineBuildName() {
81	int size = size();
82	StringBuffer buffer = new StringBuffer();
83	for (int i=0; i<size; i++) {
84		ConfigResults configResults = (ConfigResults) this.children.get(i);
85		if (configResults.isValid()) {
86			return configResults.getBaselineBuildName();
87			/* TODO (frederic) decide what return when baseline is not the same on all configs...
88			 * Currently returns the first found, but may be a comma-separated list?
89			String baselineName = configResults.getBaselineBuildName();
90			if (buffer.indexOf(baselineName) < 0) {
91				if (buffer.length() > 0) buffer.append('|');
92				buffer.append(baselineName);
93			}
94			*/
95		}
96	}
97	return buffer.toString();
98}
99
100Set getAllBuildNames() {
101	Set buildNames = new HashSet();
102	int size = size();
103	for (int i=0; i<size; i++) {
104		ConfigResults configResults = (ConfigResults) this.children.get(i);
105		List builds = configResults.getBuilds(null);
106		int length = builds.size();
107		for (int j=0; j<length; j++) {
108			buildNames.add(((BuildResults)builds.get(j)).getName());
109		}
110	}
111	return buildNames;
112}
113
114/**
115 * Return the results of the given configuration.
116 *
117 * @param config The configuration name
118 * @return The {@link ConfigResults results} for the given configuration
119 * 	or <code>null</code> if none was found.
120 */
121public ConfigResults getConfigResults(String config) {
122	return (ConfigResults) getResults(config);
123}
124
125/**
126 * Return a name which can be used as a file name to store information
127 * related to this scenario. This name does not contain the extension.
128 *
129 * @return The file name
130 */
131public String getFileName() {
132	if (this.fileName == null) {
133		this.fileName = "Scenario" + this.id; //$NON-NLS-1$
134	}
135	return this.fileName;
136}
137
138/**
139 * Returns the scenario label. If no label exist as there's no associated summary,
140 * then the short name is returned
141 *
142 * @return The label of the scenario or it's short name if no summary exists
143 */
144public String getLabel() {
145	return this.label == null ? getShortName() : this.label;
146}
147
148/**
149 * Returns the short name of the scenario. Short name is the name scenario
150 * from which package declaration has been removed.
151 *
152 * @return The scenario short name
153 */
154public String getShortName() {
155	if (this.shortName == null) {
156		// Remove class name qualification
157		int testSeparator = this.name.indexOf('#');
158		boolean hasClassName = testSeparator >= 0;
159		if (!hasClassName) {
160			testSeparator = this.name.lastIndexOf('.');
161			if (testSeparator <= 0) {
162				return this.shortName = this.name;
163			}
164		}
165		int classSeparator = this.name.substring(0, testSeparator).lastIndexOf('.');
166		if (classSeparator < 0) {
167			return this.shortName = this.name;
168		}
169		int length = this.name.length();
170		String testName = this.name.substring(classSeparator+1, length);
171		if (!hasClassName && testName.startsWith("test.")) { // specific case for swt... //$NON-NLS-1$
172			testName = testName.substring(5);
173		}
174
175		// Remove qualification from test name
176		StringTokenizer tokenizer = new StringTokenizer(testName, " :,", true); //$NON-NLS-1$
177		StringBuffer buffer = new StringBuffer(tokenizer.nextToken());
178		while (tokenizer.hasMoreTokens()) {
179			String token = tokenizer.nextToken();
180			char fc = token.charAt(0);
181			while (fc == ' ' || fc == ',' || fc == ':') {
182				buffer.append(token); // add the separator
183				token = tokenizer.nextToken();
184				fc = token.charAt(0);
185			}
186			int last = token.lastIndexOf('.');
187			if (last >= 3) {
188				int first = token .indexOf('.');
189				if (first == last) {
190					buffer.append(token);
191				} else {
192					buffer.append(token.substring(last+1));
193				}
194			} else {
195				buffer.append(token);
196			}
197		}
198		this.shortName = buffer.toString();
199	}
200	return this.shortName;
201}
202
203/**
204 * Returns whether one of the scenario's config has a summary or not.
205 *
206 * @return <code>true</code> if one of the scenario's config has a summary
207 * 	<code>false</code> otherwise.
208 */
209public boolean hasSummary() {
210	int size = size();
211	for (int i=0; i<size; i++) {
212		ConfigResults configResults = (ConfigResults) this.children.get(i);
213		BuildResults currentBuildResults = configResults.getCurrentBuildResults();
214		if (currentBuildResults != null && currentBuildResults.hasSummary()) return true;
215	}
216	return false;
217}
218
219/* (non-Javadoc)
220 * @see org.eclipse.test.internal.performance.results.AbstractResults#hashCode()
221 */
222public int hashCode() {
223	return this.id;
224}
225
226/**
227 * Returns whether the current scenario is valid or not.
228 *
229 * @return <code>true</code> if all the builds contained in the database are
230 * 	known by the scenario (ie. at least one its configuration knows each of the
231 * 	db builds), <code>false</code> otherwise.
232 */
233public boolean isValid() {
234	int size = this.children.size();
235	for (int i=0; i<size; i++) {
236		ConfigResults configResults = (ConfigResults) this.children.get(i);
237		if (configResults.isValid()) {
238			return true;
239		}
240	}
241	return false;
242}
243
244/**
245 * Returns whether the current build of the given config has valid results or not.
246 *
247 * @param config The name of the configuration
248 * @return <code>true</code> if the build has valid results
249 * 	<code>false</code> otherwise.
250 */
251public boolean isValid(String config) {
252	return getResults(config) != null;
253}
254
255/**
256 * Returns whether the current scenario knows a build or not.
257 *
258 * @param buildName The name of the build
259 * @return <code>true</code> if the at least one of scenario configuration
260 * 	knows the given build, <code>false</code> otherwise.
261 */
262public boolean knowsBuild(String buildName) {
263	String[] buildNames = buildName == null
264		? DB_Results.getBuilds()
265		: new String[] { buildName };
266	Set scenarioBuilds = getAllBuildNames();
267	int length = buildNames.length;
268	for (int i=0; i<length; i++) {
269		if (!scenarioBuilds.contains(buildNames[i])) {
270			return false;
271		}
272	}
273	return true;
274}
275
276/*
277 * Read scenario results information from database.
278 *
279void read(String buildName, long lastBuildTime) {
280
281	// Get values
282	print("	+ scenario '"+getShortName()+"': values..."); //$NON-NLS-1$ //$NON-NLS-2$
283	long start = System.currentTimeMillis();
284	String configPattern = getPerformance().getConfigurationsPattern();
285	DB_Results.queryScenarioValues(this, configPattern, buildName, lastBuildTime);
286	print(timeString(System.currentTimeMillis()-start));
287
288	// Set baseline and current builds
289	print(", infos..."); //$NON-NLS-1$
290	start = System.currentTimeMillis();
291	int size = size();
292	String[] builds = buildName == null ? null : new String[] { buildName };
293	for (int i=0; i<size; i++) {
294		ConfigResults configResults = (ConfigResults) this.children.get(i);
295		configResults.completeResults(builds);
296	}
297	println(timeString(System.currentTimeMillis()-start));
298}
299*/
300
301/*
302 * Read data from a local file
303 */
304void readData(DataInputStream stream) throws IOException {
305
306	// Read data stored locally
307	int size = stream.readInt();
308	for (int i=0; i<size; i++) {
309		int config_id = stream.readInt();
310		ConfigResults configResults = (ConfigResults) getResults(config_id);
311		if (configResults == null) {
312			configResults = new ConfigResults(this, config_id);
313			addChild(configResults, true);
314		}
315		configResults.readData(stream);
316	}
317}
318
319/*
320 * Read new data from the database.
321 * This is typically needed when the build results are not in the local file...
322 *
323boolean readNewData(String lastBuildName, boolean force) {
324	if (lastBuildName == null) {
325		read(null, -1);
326		return true;
327	}
328	PerformanceResults performanceResults = getPerformance();
329	String lastBuildDate = getBuildDate(lastBuildName, getBaselinePrefix());
330	if (force || performanceResults.getBuildDate().compareTo(lastBuildDate) > 0) {
331		long lastBuildTime = 0;
332	    try {
333		    lastBuildTime = DATE_FORMAT.parse(lastBuildDate).getTime();
334	    } catch (ParseException e) {
335		    // should not happen
336	    }
337	    read(lastBuildName, lastBuildTime);
338		return true;
339	}
340	return false;
341}
342*/
343
344/*
345 * Set value from database information.
346 */
347void setInfos(int config_id, int build_id, int summaryKind, String comment) {
348	ConfigResults configResults = (ConfigResults) getResults(config_id);
349	if (configResults == null) {
350		configResults = new ConfigResults(this, config_id);
351		addChild(configResults, true);
352	}
353	configResults.setInfos(build_id, summaryKind, comment);
354}
355
356/*
357 * Set value from database information.
358 */
359void setValue(int build_id, int dim_id, int config_id, int step, long value) {
360	ConfigResults configResults = (ConfigResults) getResults(config_id);
361	if (configResults == null) {
362		configResults = new ConfigResults(this, config_id);
363		addChild(configResults, true);
364	}
365	configResults.setValue(build_id, dim_id, step, value);
366}
367
368/*
369 * Read scenario results information from database.
370 */
371boolean updateBuild(String buildName, boolean force) {
372
373	if (!force && knowsBuild(buildName)) {
374		return false;
375	}
376
377	// Get values
378	print("	+ scenario '"+getShortName()+"': values..."); //$NON-NLS-1$ //$NON-NLS-2$
379	long start = System.currentTimeMillis();
380	String configPattern = getPerformance().getConfigurationsPattern();
381	DB_Results.queryScenarioValues(this, configPattern, buildName);
382	print(Util.timeString(System.currentTimeMillis()-start));
383
384	// Set baseline and current builds
385	print(", infos..."); //$NON-NLS-1$
386	start = System.currentTimeMillis();
387	int size = size();
388	String[] builds = buildName == null ? null : new String[] { buildName };
389	for (int i=0; i<size; i++) {
390		ConfigResults configResults = (ConfigResults) this.children.get(i);
391		configResults.completeResults(builds);
392	}
393	println(Util.timeString(System.currentTimeMillis()-start));
394	return true;
395}
396
397void write(DataOutputStream stream) throws IOException {
398	int size = size();
399	stream.writeInt(this.id);
400	stream.writeInt(size);
401	for (int i=0; i<size; i++) {
402		ConfigResults configResults = (ConfigResults) this.children.get(i);
403		configResults.write(stream);
404	}
405}
406
407}
408