1402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll/*******************************************************************************
2402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll * Copyright (c) 2000, 2009 IBM Corporation and others.
3402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll * All rights reserved. This program and the accompanying materials
4402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll * are made available under the terms of the Eclipse Public License v1.0
5402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll * which accompanies this distribution, and is available at
6402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll * http://www.eclipse.org/legal/epl-v10.html
7402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll *
8402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll * Contributors:
9402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll *     IBM Corporation - initial API and implementation
10402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll *******************************************************************************/
11402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollpackage org.eclipse.test.performance.ui;
12402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
13402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport java.io.BufferedOutputStream;
14402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport java.io.File;
15402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport java.io.FileNotFoundException;
16402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport java.io.FileOutputStream;
17402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport java.io.IOException;
18402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport java.io.OutputStream;
19402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport java.net.URL;
20402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport java.text.DecimalFormat;
21402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport java.text.NumberFormat;
22402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport java.util.Arrays;
23402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport java.util.Calendar;
24402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport java.util.Enumeration;
25402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport java.util.HashMap;
26402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
27402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport org.eclipse.swt.SWT;
28402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport org.eclipse.swt.graphics.Image;
29402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport org.eclipse.swt.graphics.ImageData;
30402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport org.eclipse.swt.graphics.ImageLoader;
31402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport org.eclipse.swt.graphics.PaletteData;
32402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport org.eclipse.swt.graphics.RGB;
33402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport org.eclipse.test.internal.performance.PerformanceTestPlugin;
34402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport org.eclipse.test.internal.performance.db.Variations;
35402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport org.eclipse.test.internal.performance.results.utils.Util;
36402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollimport org.osgi.framework.Bundle;
37402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
38402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
39402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollpublic class Utils {
40402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
41402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static double STANDARD_ERROR_THRESHOLD = 0.03; // 3%
42402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	static final NumberFormat PERCENT_FORMAT = NumberFormat.getPercentInstance();
43402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	static {
44402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		PERCENT_FORMAT.setMaximumFractionDigits(1);
45402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	}
46402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	static final DecimalFormat DEVIATION_FORMAT = (DecimalFormat) NumberFormat.getPercentInstance();
47402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	static {
48402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		DEVIATION_FORMAT.setMaximumFractionDigits(1);
49402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		DEVIATION_FORMAT.setMinimumFractionDigits(1);
50402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		DEVIATION_FORMAT.setPositivePrefix("+");
51402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		DEVIATION_FORMAT.setNegativePrefix("- ");
52402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	}
53402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	static final DecimalFormat STDERR_FORMAT = (DecimalFormat) NumberFormat.getNumberInstance();
54402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	static {
55402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		STDERR_FORMAT.setMaximumFractionDigits(1);
56402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		STDERR_FORMAT.setMinimumFractionDigits(1);
57402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		STDERR_FORMAT.setMultiplier(100);
58402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	}
59402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static String STANDARD_ERROR_THRESHOLD_STRING = PERCENT_FORMAT.format(STANDARD_ERROR_THRESHOLD);
60402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
61402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	// Image files
62402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static String UNKNOWN_IMAGE="images/Unknown.gif";
63402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static String OK_IMAGE="images/OK.gif";
64402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static String OK_IMAGE_WARN="images/OK_caution.gif";
65402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static String FAIL_IMAGE="images/FAIL.gif";
66402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static String FAIL_IMAGE_WARN="images/FAIL_caution.gif";
67402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static String FAIL_IMAGE_EXPLAINED="images/FAIL_greyed.gif";
68402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static String LIGHT="images/light.gif";
69402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static String WARNING_OBJ="images/warning_obj.gif";
70402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
71402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	// Java script files
72402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static String TOOLTIP_SCRIPT = "scripts/ToolTip.js";
73402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static String TOOLTIP_STYLE = "scripts/ToolTip.css";
74402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static String FINGERPRINT_SCRIPT = "scripts/Fingerprints.js";
75402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
76402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	// Doc files
77402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static String HELP = "doc/help.html";
78402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
79402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	// Status
80402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static int OK = 0;
81402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static int NAN = 0x1;
82402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static int ERR = 0x2;
83402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
84402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	/**
85402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * Return <html><head><meta http-equiv="Content-Type"
86402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 *         content="text/html; charset=iso-8859-1">
87402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 */
88402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static String HTML_OPEN = "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\">\n";
89402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
90402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	/**
91402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * Return "&lt;/html&gt;".
92402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 */
93402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static String HTML_CLOSE = "</html>\n";
94402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
95402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	/**
96402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * Default style-sheet used on eclipse.org
97402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 */
98402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public final static String HTML_DEFAULT_CSS = "<style type=\"text/css\">" + "p, table, td, th {  font-family: arial, helvetica, geneva; font-size: 10pt}\n"
99402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			+ "pre {  font-family: \"Courier New\", Courier, mono; font-size: 10pt}\n" + "h2 { font-family: arial, helvetica, geneva; font-size: 18pt; font-weight: bold ; line-height: 14px}\n"
100402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			+ "code {  font-family: \"Courier New\", Courier, mono; font-size: 10pt}\n" + "sup {  font-family: arial,helvetica,geneva; font-size: 10px}\n"
101402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			+ "h3 {  font-family: arial, helvetica, geneva; font-size: 14pt; font-weight: bold}\n" + "li {  font-family: arial, helvetica, geneva; font-size: 10pt}\n"
102402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			+ "h1 {  font-family: arial, helvetica, geneva; font-size: 28px; font-weight: bold}\n"
103402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			+ "body {  font-family: arial, helvetica, geneva; font-size: 10pt; clip:   rect(   ); margin-top: 5mm; margin-left: 3mm}\n"
104402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			+ ".indextop { font-size: x-large;; font-family: Verdana, Arial, Helvetica, sans-serif; font-weight: bold}\n"
105402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			+ ".indexsub { font-size: xx-small;; font-family: Arial, Helvetica, sans-serif; color: #8080FF}\n" + "</style>\n\n";
106402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
107402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	/**
108402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * Creates a Variations object using build id pattern, config and jvm.
109402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 *
110402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * @param buildIdPattern
111402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * @param config
112402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * @param jvm
113402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 */
114402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public static Variations getVariations(String buildIdPattern, String config, String jvm) {
115402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		String buildIdPatterns = buildIdPattern.replace(',', '%');
116402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		Variations variations = new Variations();
117402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		variations.put(PerformanceTestPlugin.CONFIG, config);
118402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		variations.put(PerformanceTestPlugin.BUILD, buildIdPatterns);
119402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		variations.put("jvm", jvm);
120402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		return variations;
121402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	}
122402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
123402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	/**
124402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * Copy all bundle files contained in the given path
125402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 */
126402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public static void copyBundleFiles(Bundle bundle, String path, String pattern, File output) {
127402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		Enumeration imageFiles = bundle.findEntries(path, pattern, false);
128402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		while (imageFiles.hasMoreElements()) {
129402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			URL url = (URL) imageFiles.nextElement();
130402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			try {
131402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				File outputFile = new File(output, url.getFile());
132402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				if (!outputFile.getParentFile().exists()) {
133402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll					outputFile.getParentFile().mkdirs();
134402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				}
135402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				Util.copyStream(url.openStream(), outputFile);
136402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			} catch (IOException e) {
137402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				// TODO Auto-generated catch block
138402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				e.printStackTrace();
139402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			}
140402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		}
141402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	}
142402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
143402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	/**
144402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * Downsample Image to 8 bit depth format so that the resulting image data
145402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * can be saved to GIF. Note. If the source image contains photo quality
146402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * content with more than 256 colours, resulting data will look very poor.
147402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 */
148402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	static int closest(RGB[] rgbs, int n, RGB rgb) {
149402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		int minDist = 256 * 256 * 3;
150402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		int minIndex = 0;
151402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		for (int i = 0; i < n; ++i) {
152402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			RGB rgb2 = rgbs[i];
153402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			int da = rgb2.red - rgb.red;
154402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			int dg = rgb2.green - rgb.green;
155402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			int db = rgb2.blue - rgb.blue;
156402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			int dist = da * da + dg * dg + db * db;
157402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			if (dist < minDist) {
158402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				minDist = dist;
159402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				minIndex = i;
160402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			}
161402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		}
162402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		return minIndex;
163402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	}
164402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
165402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	static class ColorCounter implements Comparable {
166402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		RGB rgb;
167402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
168402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		int count;
169402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
170402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		public int compareTo(Object o) {
171402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			return ((ColorCounter) o).count - this.count;
172402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		}
173402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	}
174402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
175402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public static ImageData downSample(Image image) {
176402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		ImageData data = image.getImageData();
177402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		if (!data.palette.isDirect && data.depth <= 8)
178402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			return data;
179402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
180402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		// compute a histogram of color frequencies
181402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		HashMap freq = new HashMap();
182402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		int width = data.width;
183402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		int[] pixels = new int[width];
184402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		int[] maskPixels = new int[width];
185402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		for (int y = 0, height = data.height; y < height; ++y) {
186402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			data.getPixels(0, y, width, pixels, 0);
187402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			for (int x = 0; x < width; ++x) {
188402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				RGB rgb = data.palette.getRGB(pixels[x]);
189402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				ColorCounter counter = (ColorCounter) freq.get(rgb);
190402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				if (counter == null) {
191402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll					counter = new ColorCounter();
192402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll					counter.rgb = rgb;
193402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll					freq.put(rgb, counter);
194402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				}
195402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				counter.count++;
196402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			}
197402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		}
198402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
199402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		// sort colors by most frequently used
200402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		ColorCounter[] counters = new ColorCounter[freq.size()];
201402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		freq.values().toArray(counters);
202402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		Arrays.sort(counters);
203402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
204402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		// pick the most frequently used 256 (or fewer), and make a palette
205402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		ImageData mask = null;
206402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		if (data.transparentPixel != -1 || data.maskData != null) {
207402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			mask = data.getTransparencyMask();
208402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		}
209402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		int n = Math.min(256, freq.size());
210402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		RGB[] rgbs = new RGB[n + (mask != null ? 1 : 0)];
211402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		for (int i = 0; i < n; ++i)
212402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			rgbs[i] = counters[i].rgb;
213402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		if (mask != null) {
214402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			rgbs[rgbs.length - 1] = data.transparentPixel != -1 ? data.palette.getRGB(data.transparentPixel) : new RGB(255, 255, 255);
215402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		}
216402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		PaletteData palette = new PaletteData(rgbs);
217402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
218402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		// create a new image using the new palette:
219402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		// for each pixel in the old image, look up the best matching
220402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		// index in the new palette
221402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		ImageData newData = new ImageData(width, data.height, 8, palette);
222402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		if (mask != null)
223402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			newData.transparentPixel = rgbs.length - 1;
224402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		for (int y = 0, height = data.height; y < height; ++y) {
225402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			data.getPixels(0, y, width, pixels, 0);
226402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			if (mask != null)
227402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				mask.getPixels(0, y, width, maskPixels, 0);
228402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			for (int x = 0; x < width; ++x) {
229402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				if (mask != null && maskPixels[x] == 0) {
230402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll					pixels[x] = rgbs.length - 1;
231402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				} else {
232402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll					RGB rgb = data.palette.getRGB(pixels[x]);
233402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll					pixels[x] = closest(rgbs, n, rgb);
234402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				}
235402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			}
236402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			newData.setPixels(0, y, width, pixels, 0);
237402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		}
238402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		return newData;
239402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	}
240402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
241402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	/**
242402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * Returns the date/time from the build id in format yyyymmddhm
243402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 *
244402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * @param buildId
245402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * @return date/time in format YYYYMMDDHHMM, ie. 200504060010
246402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 */
247402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public static long getDateFromBuildID(String buildId) {
248402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		return getDateFromBuildID(buildId, false);
249402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	}
250402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
251402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public static long getDateFromBuildID(String buildId, boolean matchLast) {
252402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		Calendar calendar = Calendar.getInstance();
253402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
254402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		if (buildId.indexOf('_') != -1) {
255402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			String[] buildIdParts = buildId.split("_");
256402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
257402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			int buildIdSegment = 1;
258402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			if (matchLast)
259402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				buildIdSegment = buildIdParts.length - 1;
260402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			// if release build, expect <release>_<release date and
261402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			// timestamp>_<date and timestamp test ran>
262402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			// use test date and time for plotting
263402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			int year = Integer.parseInt(buildIdParts[buildIdSegment].substring(0, 4));
264402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			int month = Integer.parseInt(buildIdParts[buildIdSegment].substring(4, 6)) - 1;
265402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			int date = Integer.parseInt(buildIdParts[buildIdSegment].substring(6, 8));
266402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			int hours = Integer.parseInt(buildIdParts[buildIdSegment].substring(8, 10));
267402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			int min = Integer.parseInt(buildIdParts[buildIdSegment].substring(10, 12));
268402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
269402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			calendar.set(year, month, date, hours, min);
270402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			return calendar.getTimeInMillis();
271402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
272402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		} else if (buildId.indexOf('-') != -1) {
273402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			// if regular build, expect <buildType><date>-<time> format
274402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			String[] buildIdParts = buildId.split("-");
275402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			int year = Integer.parseInt(buildIdParts[0].substring(1, 5));
276402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			int month = Integer.parseInt(buildIdParts[0].substring(5, 7)) - 1;
277402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			int date = Integer.parseInt(buildIdParts[0].substring(7, 9));
278402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			int hours = Integer.parseInt(buildIdParts[1].substring(0, 2));
279402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			int min = Integer.parseInt(buildIdParts[1].substring(2, 4));
280402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			calendar.set(year, month, date, hours, min);
281402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
282402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			return calendar.getTimeInMillis();
283402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		}
284402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
285402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		return -1;
286402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	}
287402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
288402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	/**
289402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * Returns a message corresponding to given statistics.
290402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 *
291402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * @param resultStats The value with its standard error
292402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * @param full
293402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * @return The failure message. May be empty if stats are good...
294402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 */
295402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public static String failureMessage(double[] resultStats, boolean full) {
296402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		StringBuffer buffer = new StringBuffer();
297402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		int level = confidenceLevel(resultStats);
298402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll//		boolean isWarn = (level & WARN) != 0;
299402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		boolean isErr = (level & ERR) != 0;
300402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		if (full) {
301402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			if (isErr) {
302402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				buffer.append("*** WARNING ***  ");
303402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 			buffer.append(Messages.bind(Messages.standardError, PERCENT_FORMAT.format(resultStats[1]), STANDARD_ERROR_THRESHOLD_STRING));
304402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			}
305402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			return buffer.toString();
306402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		}
307402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		if (resultStats != null) {
308402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			double deviation = resultStats[0];
309402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			buffer.append("<font color=\"#0000FF\" size=\"1\">");
310402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			if (Double.isNaN(deviation) || Double.isInfinite(deviation)) {
311402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 			buffer.append(" [n/a]");
312402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll 			} else {
313402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				double stderr = resultStats[1];
314402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				deviation = Math.abs(deviation)<0.001 ? 0 : -deviation;
315402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 			if (Double.isNaN(stderr) || Double.isInfinite(stderr)) {
316402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		 			buffer.append(DEVIATION_FORMAT.format(deviation));
317402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll					buffer.append("</font><font color=\"#DDDD00\" size=\"1\"> ");
318402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		 			buffer.append(" [n/a]");
319402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 			} else {
320402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		 			buffer.append(DEVIATION_FORMAT.format(deviation));
321402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 				buffer.append(" [&#177;");
322402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 				buffer.append(STDERR_FORMAT.format(Math.abs(stderr)));
323402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 				buffer.append(']');
324402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 			}
325402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll 			}
326402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			buffer.append("</font>");
327402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		}
328402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		return buffer.toString();
329402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	}
330402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
331402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	/**
332402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * Returns the confidence level for given statistics:
333402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * <ul>
334402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * <li>{@link #NAN}: if the value is infinite or not a number</li>
335402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * <li>{@link #ERR}: if the standard error is over the expected threshold ({@link #STANDARD_ERROR_THRESHOLD})</li>
336402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * <li>{@link #OK}: in all other cases</li>
337402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * </ul>
338402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 *
339402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * @param resultStats array of 2 doubles, the former is the average value and
340402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * 	the latter is the standard error made while computing the average.
341402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * @return a value telling caller the level of confidence of the provided value
342402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 */
343402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public static int confidenceLevel(double[] resultStats) {
344402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		int level = OK;
345402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll 		if (resultStats != null){
346402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			if (Double.isNaN(resultStats[0]) || Double.isInfinite(resultStats[0])) {
347402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				level = NAN;
348402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll 			} else {
349402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll//	 			if (resultStats[1] >= (STANDARD_ERROR_THRESHOLD/2)) { // warns standard error higher than the half of authorized threshold
350402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll//	 				level |= WARN;
351402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll//	 			}
352402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 			if (resultStats[1] >= STANDARD_ERROR_THRESHOLD) { // standard error higher than the authorized threshold
353402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 				level = ERR;
354402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 			}
355402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll 			}
356402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll 		}
357402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		return level;
358402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	}
359402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
360402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	/**
361402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * Get an icon image corresponding to a given level of confidence and explanation.
362402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 *
363402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * @param confidence the confiden level
364402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * @param hasExplanation flags indicates whether the confidence may be tempered by an explanation
365402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 * @return Corresponding image
366402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	 */
367402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	public static String getImage(int confidence, boolean scenarioFailed, boolean hasExplanation) {
368402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	    String image = null;
369402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
370402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	    if (scenarioFailed) {
371402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	    	if (hasExplanation) {
372402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		    	image = FAIL_IMAGE_EXPLAINED;
373402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		    } else if ((confidence & ERR) != 0) {
374402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll    			image = FAIL_IMAGE_WARN;
375402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		    } else {
376402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll    			image = FAIL_IMAGE;
377402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		    }
378402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	    } else if ((confidence & NAN) != 0) {
379402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			image = UNKNOWN_IMAGE;
380402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	    } else if ((confidence & ERR) != 0) {
381402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	   		image = OK_IMAGE_WARN;
382402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	    } else {
383402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll   			image = OK_IMAGE;
384402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	    }
385402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	    return image;
386402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll    }
387402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
388402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll/**
389402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll * @param outputFile
390402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll * @param image
391402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll */
392402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Mollpublic static void saveImage(File outputFile, Image image) {
393402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	// Save image
394402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	ImageData data = downSample(image);
395402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	ImageLoader imageLoader = new ImageLoader();
396402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	imageLoader.data = new ImageData[] { data };
397402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
398402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	OutputStream out = null;
399402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	try {
400402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		out = new BufferedOutputStream(new FileOutputStream(outputFile));
401402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		imageLoader.save(out, SWT.IMAGE_GIF);
402402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	} catch (FileNotFoundException e) {
403402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		e.printStackTrace();
404402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	} finally {
405402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		image.dispose();
406402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		if (out != null) {
407402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			try {
408402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				out.close();
409402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			} catch (IOException e1) {
410402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll				// silently ignored
411402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll			}
412402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll		}
413402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll	}
414402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll}
415402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll
416402794e73aed8611d62eb4b01cd155e2d76fcb87Raphael Moll}