/*******************************************************************************
 * Copyright (c) 2000, 2004 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.core.tests.compiler.regression;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.text.MessageFormat;

import junit.framework.Test;
import junit.framework.TestSuite;

import org.eclipse.jdt.core.tests.util.Util;
import org.eclipse.jdt.internal.compiler.batch.Main;

public class BatchCompilerTest extends AbstractRegressionTest {
	public static final String OUTPUT_DIR_PLACEHOLDER = "---OUTPUT_DIR_PLACEHOLDER---";
	static final String JRE_HOME_DIR = Util.getJREDirectory();
	
public BatchCompilerTest(String name) {
	super(name);
}
public static Test suite() {
	if (false) {
		TestSuite suite = new TestSuite();
		suite.addTest(new BatchCompilerTest("test032"));
		return suite;
	}
	if (false) {
		TestSuite suite = new TestSuite();
		for (int i = 23; i < 27; i++) 
		  suite.addTest(new BatchCompilerTest("test0" + String.valueOf(i)));
		return suite;
	}
	return setupSuite(testClass());
	// TODO find a way to reduce the number of command line tests to one per 
	//      test run (aka do not add 1.3, 1.4, 1.5 supplementary level)
}

	/**
	 * Run a compilation test that is expected to complete successfully and
	 * compare the outputs to expected ones.
	 * 
	 * @param testFiles
	 *            the source files, given as a suite of file name, file content;
	 *            file names are relative to the output directory
	 * @param commandLine
	 *            the command line to pass to
	 *            {@link Main#compile(String) Main#compile}
	 * @param expectedSuccessOutOutputString
	 *            the expected contents of the standard output stream; pass null
	 *            to bypass the comparison
	 * @param expectedSuccessErrOutputString
	 *            the expected contents of the standard error output stream;
	 *            pass null to bypass the comparison
	 * @param shouldFlushOutputDirectory
	 *            pass true to get the output directory flushed before the test
	 *            runs
	 */
	protected void runConformTest(String[] testFiles, String commandLine,
			String expectedSuccessOutOutputString,
			String expectedSuccessErrOutputString,
			boolean shouldFlushOutputDirectory) {
		runTest(true, testFiles, commandLine, expectedSuccessOutOutputString,
				expectedSuccessErrOutputString, shouldFlushOutputDirectory);
	}

	/**
	 * Run a compilation test that is expected to fail and compare the outputs
	 * to expected ones.
	 * 
	 * @param testFiles
	 *            the source files, given as a suite of file name, file content;
	 *            file names are relative to the output directory
	 * @param commandLine
	 *            the command line to pass to
	 *            {@link Main#compile(String) Main#compile}
	 * @param expectedFailureOutOutputString
	 *            the expected contents of the standard output stream; pass null
	 *            to bypass the comparison
	 * @param expectedFailureErrOutputString
	 *            the expected contents of the standard error output stream;
	 *            pass null to bypass the comparison
	 * @param shouldFlushOutputDirectory
	 *            pass true to get the output directory flushed before the test
	 *            runs
	 */
	protected void runNegativeTest(String[] testFiles, String commandLine,
			String expectedFailureOutOutputString,
			String expectedFailureErrOutputString,
			boolean shouldFlushOutputDirectory) {
		runTest(false, testFiles, commandLine, expectedFailureOutOutputString,
				expectedFailureErrOutputString, shouldFlushOutputDirectory);
	}

	/**
	 * Worker method for runConformTest and runNegativeTest.
	 * 
	 * @param shouldCompileOK
	 *            set to true if the compiler should compile the given sources
	 *            without errors
	 * @param testFiles
	 *            the source files, given as a suite of file name, file content;
	 *            file names are relative to the output directory
	 * @param commandLine
	 *            the command line to pass to
	 *            {@link Main#compile(String) Main#compile}
	 * @param expectedOutOutputString
	 *            the expected contents of the standard output stream; pass null
	 *            to bypass the comparison
	 * @param expectedErrOutputString
	 *            the expected contents of the standard error output stream;
	 *            pass null to bypass the comparison
	 * @param shouldFlushOutputDirectory
	 *            pass true to get the output directory flushed before the test
	 *            runs
	 */
	private void runTest(boolean shouldCompileOK, String[] testFiles, String commandLine,
			String expectedOutOutputString,
			String expectedErrOutputString,
			boolean shouldFlushOutputDirectory) {
		File outputDirectory = new File(OUTPUT_DIR);
		if (shouldFlushOutputDirectory)
			Util.flushDirectoryContent(outputDirectory);
		try {
			if (!outputDirectory.isDirectory()) {
				outputDirectory.mkdirs();
			}
			PrintWriter sourceFileWriter;
			for (int i = 0; i < testFiles.length; i += 2) {
				String fileName = OUTPUT_DIR + File.separator + testFiles[i];
				File file = new File(fileName), innerOutputDirectory = file
						.getParentFile();
				if (!innerOutputDirectory.isDirectory()) {
					innerOutputDirectory.mkdirs();
				}
				sourceFileWriter = new PrintWriter(new FileOutputStream(file));
				sourceFileWriter.write(testFiles[i + 1]);
				sourceFileWriter.close();
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
		String printerWritersNameRoot = OUTPUT_DIR + File.separator + testName();
		String outFileName = printerWritersNameRoot + "out.txt", 
			   errFileName = printerWritersNameRoot + "err.txt";
		Main batchCompiler;
		try {
			batchCompiler = new Main(new PrintWriter(new FileOutputStream(
					outFileName)), new PrintWriter(new FileOutputStream(
					errFileName)), false);
		} catch (FileNotFoundException e) {
			System.out.println(getClass().getName() + '#' + getName());
			e.printStackTrace();
			throw new RuntimeException(e);
		}
		boolean compileOK;
		try {
			final String[] tokenizeCommandLine = Main.tokenize(commandLine);
			compileOK = batchCompiler.compile(tokenizeCommandLine);
		} catch (RuntimeException e) {
			compileOK = false;
			System.out.println(getClass().getName() + '#' + getName());
			e.printStackTrace();
			throw e;
		}
		String outOutputString = Util.fileContent(outFileName), 
		       errOutputString = Util.fileContent(errFileName);
		boolean compareOK = false;
		if (compileOK == shouldCompileOK) {
			compareOK = semiNormalizedComparison(expectedOutOutputString,
					outOutputString, outputDirNormalizer)
					&& semiNormalizedComparison(expectedErrOutputString,
							errOutputString, outputDirNormalizer);
		}
		if (compileOK != shouldCompileOK || !compareOK) {
			System.out.println(getClass().getName() + '#' + getName());
			for (int i = 0; i < testFiles.length; i += 2) {
				System.out.print(testFiles[i]);
				System.out.println(" [");
				System.out.println(testFiles[i + 1]);
				System.out.println("]");
			}
		}
		if (compileOK != shouldCompileOK)
			System.out.println(errOutputString);
		if (compileOK == shouldCompileOK && !compareOK) {
			System.out.println(
					    "------------ [START OUT] ------------\n"
					+   "------------- Expected: -------------\n"
					+ expectedOutOutputString
					+ "\n------------- but was:  -------------\n"
					+ outOutputString
					+ "\n--------- (cut and paste:) ----------\n"
					+ Util.displayString(outputDirNormalizer
							.normalized(outOutputString))
					+ "\n------------- [END OUT] -------------\n"
					+   "------------ [START ERR] ------------\n"
					+   "------------- Expected: -------------\n"
					+ expectedErrOutputString
					+ "\n------------- but was:  -------------\n"
					+ errOutputString
					+ "\n--------- (cut and paste:) ----------\n"
					+ Util.displayString(outputDirNormalizer
							.normalized(errOutputString))
					+ "\n------------- [END ERR] -------------\n");
		}
		if (shouldCompileOK)
			assertTrue("Unexpected problems: " + errOutputString, compileOK);
		else
			assertTrue("Unexpected success: " + errOutputString, !compileOK);
		assertTrue("Unexpected output for invocation with arguments ["
				+ commandLine + "]:\n--[START]--\n" + outOutputString + "\n"
				+ errOutputString + "\n---[END]---\n", compareOK);
	}
	
	/**
	 * Abstract normalizer for output comparison. This class merely embodies a
	 * chain of responsibility, plus the signature of the method of interest
	 * here, that is {@link #normalized(String) normalized}.
	 */
	private static abstract class Normalizer {
		private Normalizer nextInChain;
		Normalizer(Normalizer nextInChain) {
			this.nextInChain = nextInChain;
		}
		String normalized(String originalValue) {
			String result;
			if (nextInChain == null)
				result = Util.convertToIndependantLineDelimiter(originalValue);
			else
				result = nextInChain.normalized(originalValue);
			return result;
		}
	}

	/**
	 * This normalizer replaces occurrences of a given string with a given
	 * placeholder.
	 */
	private static class StringNormalizer extends Normalizer {
		private String match;
		private int matchLength;
		private String placeholder;
		StringNormalizer(Normalizer nextInChain, String match, String placeholder) {
			super(nextInChain);
			this.match = match;
			this.matchLength = match.length();
			this.placeholder = placeholder;
		}
		String normalized(String originalValue) {
			String result;
			StringBuffer normalizedValueBuffer = new StringBuffer(originalValue);
			int nextOccurrenceIndex;
			while ((nextOccurrenceIndex = normalizedValueBuffer.indexOf(match)) != -1)
				normalizedValueBuffer.replace(nextOccurrenceIndex,
						nextOccurrenceIndex + matchLength, placeholder);
			result = super.normalized(normalizedValueBuffer.toString());
			return result;
		}
	}
	
	/**
	 * This normalizer replaces the whole classpaths section of a log file with
	 * a normalized placeholder. 
	 */
	private static class XMLClasspathsSectionNormalizer extends Normalizer {
		XMLClasspathsSectionNormalizer() {
			super(null);
		}
		XMLClasspathsSectionNormalizer(Normalizer nextInChain) {
			super(nextInChain);
		}
		String normalized(String originalValue) {
			String result;
			StringBuffer normalizedValueBuffer = new StringBuffer(originalValue);
			int classpathsStartTagStart = normalizedValueBuffer
					.indexOf("<classpaths>"), classpathsEndTagStart = normalizedValueBuffer
					.indexOf("</classpaths>");
			if (classpathsStartTagStart != -1 && classpathsEndTagStart != -1
					&& classpathsStartTagStart < classpathsEndTagStart)
				normalizedValueBuffer.replace(classpathsStartTagStart + 12,
						classpathsEndTagStart, "NORMALIZED SECTION");
			result = super.normalized(normalizedValueBuffer.toString());
			return result;
		}
	}

	/**
	 * This normalizer removes a selected range of lines from a log file.
	 */
	private static class LinesRangeNormalizer extends Normalizer {
		private int first, number;

		LinesRangeNormalizer() {
			super(null);
			first = number = 0;
		}

		LinesRangeNormalizer(Normalizer nextInChain) {
			super(nextInChain);
			first = number = 0;
		}

		/**
		 * Make a new normalizer able to suppress a range of lines delimited by
		 * "\n" sequences from a log file (or another string).
		 * 
		 * @param nextInChain
		 *            the next normalizer in the chain of responsibility; pass
		 *            null if none is needed
		 * @param firstLineToRemove
		 *            the index of the first line to remove, starting at 0
		 * @param linesNumber
		 *            the number or lines to remove; if 0, no other
		 *            transformation occurs than those operated by nextInChain
		 *            (if any)
		 */
		LinesRangeNormalizer(Normalizer nextInChain, int firstLineToRemove,
				int linesNumber) {
			super(nextInChain);
			first = firstLineToRemove;
			number = linesNumber >= 0 ? linesNumber : 0;
		}

		String normalized(String originalValue) {
			String result;
			if (number == 0 || originalValue.length() == 0)
				result = super.normalized(originalValue);
			else {
				final int START = 0, KEEPING = 1, KEEPING_R = 2, SKIPING = 3, SKIPING_R = 4, END = 5, ERROR = 6;
				int state = START, currentLineIndex = 0, currentCharIndex = 0, sourceLength;
				char currentChar = '\0';
				if (first <= 0)
					state = SKIPING;
				else
					state = KEEPING;
				StringBuffer normalizedValueBuffer = new StringBuffer(), source = new StringBuffer(
						originalValue);
				sourceLength = source.length();
				while (state != END && state != ERROR) {
					if (currentCharIndex < sourceLength) {
						currentChar = source.charAt(currentCharIndex++);
						switch (currentChar) {
						case '\r':
							switch (state) {
							case KEEPING:
								normalizedValueBuffer.append(currentChar);
								state = KEEPING_R;
								break;
							case SKIPING:
								state = SKIPING_R;
								break;
							default:
								state = ERROR;
							}
							break;
						case '\n':
							currentLineIndex++;
							switch (state) {
							case KEEPING: // tolerate Linux line delimiters
							case KEEPING_R:
								normalizedValueBuffer.append(currentChar);
								if (currentLineIndex == first) {
									state = SKIPING;
								}
								break;
							case SKIPING: // tolerate Linux line delimiters
							case SKIPING_R:
								// in effect, we tolerate too big first and number
								// values
								if (currentLineIndex >= first + number) {
									if (currentCharIndex < sourceLength)
										normalizedValueBuffer.append(source
												.substring(currentCharIndex));
									state = END;
								}
								break;
							default:
								state = ERROR;
							}
							break;
						default:
							switch (state) {
							case KEEPING:
								normalizedValueBuffer.append(currentChar);
								break;
							case SKIPING:
								break;
							default:
								state = ERROR;
							}
	
						}
					} 
					else if (currentChar == '\n')
						state = END;
					else
						state = ERROR;
				}
				if (state == ERROR)
					normalizedValueBuffer
							.append("UNEXPECTED ERROR in LinesRangeNormalizer");
				result = super.normalized(normalizedValueBuffer.toString());
			}
			return result;
		}
	}

	/**
	 * Normalizer instance that replaces occurrences of OUTPUT_DIR with 
	 * OUTPUT_DIR_PLACEHOLDER.
	 */
	private static Normalizer outputDirNormalizer = new StringNormalizer(null,
			OUTPUT_DIR, OUTPUT_DIR_PLACEHOLDER);

	/**
	 * Normalizer instance for non XML log files. 
	 */
	private static Normalizer textLogsNormalizer = new StringNormalizer(
			new XMLClasspathsSectionNormalizer(new LinesRangeNormalizer(null,
					0, 1)), OUTPUT_DIR, OUTPUT_DIR_PLACEHOLDER);

	/**
	 * Normalizer instance for XML log files.
	 */
	private static Normalizer xmlLogsNormalizer = new StringNormalizer(
			new XMLClasspathsSectionNormalizer(new LinesRangeNormalizer(null,
					1, 1)), OUTPUT_DIR, OUTPUT_DIR_PLACEHOLDER);
	

	/**
	 * Return true if and only if the two strings passed as parameters compare
	 * equal, modulo the transformation of the second string by a normalizer
	 * passed in parameter. This is meant to erase the variations of subparts of
	 * the compared strings in function of the test machine, the user account,
	 * etc.
	 * 
	 * @param keep
	 *            the first string to compare, gets compared as it is
	 * @param normalize
	 *            the second string to compare, passed through the normalizer
	 *            before comparison
	 * @param normalizer
	 *            the transformation applied to normalize
	 * @return true if keep and normalize compare equal after normalize has been
	 *         normalized
	 */
	private boolean semiNormalizedComparison(String keep, String normalize,
			Normalizer normalizer) {
		if (keep == null)
			return normalize == null;
		if (normalize == null)
			return false;
		// return keep.equals(normalizer.normalized(normalize));
		return equals(keep, normalizer.normalized(normalize));
	}
private static boolean equals(String a, String b) {
	StringBuffer aBuffer = new StringBuffer(a), bBuffer = new StringBuffer(b);
	int length = aBuffer.length();
	if (length != bBuffer.length()) {
		System.err.println("a and b lengths differ");
		return false;
	}
	for (int i = 0; i < length; i++)
		if (aBuffer.charAt(i) != bBuffer.charAt(i)) {
			int beforeStart = i - 5, beforeEnd = i - 1, afterStart = i + 1, afterEnd = i + 5;
			if (beforeStart < 0) {
				beforeStart = 0;
				if (beforeEnd < 0)
					beforeEnd = 0;
			}
			if (afterEnd >= length) {
				afterEnd = length - 1;
				if (afterStart >= length) 
					afterStart = length - 1;
			}
			System.err.println("a and b differ at rank: " + i 
					+ " a: ..." + aBuffer.substring(beforeStart, beforeEnd) 
						+ "<" + aBuffer.charAt(i) + ">"
						+ aBuffer.substring(afterStart, afterEnd) + "..." 
					+ " b: ..." + bBuffer.substring(beforeStart, beforeEnd) 
						+ "<" + bBuffer.charAt(i) + ">"
						+ bBuffer.substring(afterStart, afterEnd) + "..."); 
			return false;
		}
	return true;
}

public void test001() {
	
		String commandLine = "-classpath \"D:/a folder\";d:/jdk1.4/jre/lib/rt.jar -1.4 -preserveAllLocals -g -verbose d:/eclipse/workspaces/development2.0/plugins/Bar/src2/ -d d:/test";
		String expected = " <-classpath> <D:/a folder;d:/jdk1.4/jre/lib/rt.jar> <-1.4> <-preserveAllLocals> <-g> <-verbose> <d:/eclipse/workspaces/development2.0/plugins/Bar/src2/> <-d> <d:/test>";
		
		String[] args = Main.tokenize(commandLine);
		StringBuffer  buffer = new StringBuffer(30);
		for (int i = 0; i < args.length; i++){
			buffer.append(" <"+args[i]+">");
		}
		String result = buffer.toString();
		//System.out.println(Util.displayString(result, 2));
		assertEquals("incorrect tokenized command line",
			expected,
			result);
}
public void test002() {
	
		String commandLine = "-classpath \"a folder\";\"b folder\"";
		String expected = " <-classpath> <a folder;b folder>";
		
		String[] args = Main.tokenize(commandLine);
		StringBuffer  buffer = new StringBuffer(30);
		for (int i = 0; i < args.length; i++){
			buffer.append(" <"+args[i]+">");
		}
		String result = buffer.toString();
		//System.out.println(Util.displayString(result, 2));
		assertEquals("incorrect tokenized command line",
			expected,
			result);
}
public void test003() {
	
		String commandLine = "-classpath \"a folder;b folder\"";
		String expected = " <-classpath> <a folder;b folder>";
		
		String[] args = Main.tokenize(commandLine);
		StringBuffer  buffer = new StringBuffer(30);
		for (int i = 0; i < args.length; i++){
			buffer.append(" <"+args[i]+">");
		}
		String result = buffer.toString();
		//System.out.println(Util.displayString(result, 2));
		assertEquals("incorrect tokenized command line",
			expected,
			result);
}
public void test004() {
	
		String commandLine = "\"d:/tmp A/\"A.java  -classpath \"d:/tmp A\";d:/jars/rt.jar -nowarn -time -g -d d:/tmp";
		String expected = " <d:/tmp A/A.java> <-classpath> <d:/tmp A;d:/jars/rt.jar> <-nowarn> <-time> <-g> <-d> <d:/tmp>";
		
		String[] args = Main.tokenize(commandLine);
		StringBuffer  buffer = new StringBuffer(30);
		for (int i = 0; i < args.length; i++){
			buffer.append(" <"+args[i]+">");
		}
		String result = buffer.toString();
		//System.out.println(Util.displayString(result, 2));
		assertEquals("incorrect tokenized command line",
			expected,
			result);
}
public void test005() {
	
		String commandLine = "\"d:/tmp A/\"A.java  -classpath d:/jars/rt.jar;\"d:/tmp A\";\"toto\" -nowarn -time -g -d d:/tmp";
		String expected = " <d:/tmp A/A.java> <-classpath> <d:/jars/rt.jar;d:/tmp A;toto> <-nowarn> <-time> <-g> <-d> <d:/tmp>";
		
		String[] args = Main.tokenize(commandLine);
		StringBuffer  buffer = new StringBuffer(30);
		for (int i = 0; i < args.length; i++){
			buffer.append(" <"+args[i]+">");
		}
		String result = buffer.toString();
		//System.out.println(Util.displayString(result, 2));
		assertEquals("incorrect tokenized command line",
			expected,
			result);
}
public void test006() {
	
		String commandLine = "\"d:/tmp A/A.java\"  -classpath d:/jars/rt.jar;\"d:/tmp A\";d:/tmpB/ -nowarn -time -g -d d:/tmp";
		String expected = " <d:/tmp A/A.java> <-classpath> <d:/jars/rt.jar;d:/tmp A;d:/tmpB/> <-nowarn> <-time> <-g> <-d> <d:/tmp>";
		
		String[] args = Main.tokenize(commandLine);
		StringBuffer  buffer = new StringBuffer(30);
		for (int i = 0; i < args.length; i++){
			buffer.append(" <"+args[i]+">");
		}
		String result = buffer.toString();
		//System.out.println(Util.displayString(result, 2));
		assertEquals("incorrect tokenized command line",
			expected,
			result);
}
// test the tester - runConformTest
public void _test007(){
	this.runConformTest(
		new String[] {
			"X.java",
			"import java.util.List;\n" + 
			"\n" + 
			"@SuppressWarnings(\"all\"//$NON-NLS-1$\n" + 
			")\n" + 
			"public class X {\n" + 
			"	public static void main(String[] args) {\n" + 
			"		if (false) {\n" + 
			"			;\n" + 
			"		} else {\n" + 
			"		}\n" + 
			"		// Zork z;\n" + 
			"	}\n" + 
			"}"
        },
        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
        + " -1.5 -g -preserveAllLocals"
        + " -bootclasspath " + JRE_HOME_DIR + "/lib/rt.jar"
        + " -cp " + JRE_HOME_DIR + "/lib/jce.jar"
        + " -verbose -warn:+deprecation,syntheticAccess,uselessTypeCheck,unsafe,finalBound,unusedLocal"
        + " -proceedOnError -referenceInfo -d \"" + OUTPUT_DIR + "\"",
        "[1 .class file generated]\n", 
        "----------\n" + 
        "1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
        " (at line 1)\n" + 
        "	import java.util.List;\n" + 
        "	       ^^^^^^^^^^^^^^\n" + 
        "The import java.util.List is never used\n" + 
        "----------\n" + 
        "1 problem (1 warning)", true);
}
// test the tester - runNegativeTest
public void _test008(){
	this.runNegativeTest(
		new String[] {
			"X.java",
			"import java.util.List;\n" + 
			"\n" + 
			"@SuppressWarnings(\"all\"//$NON-NLS-1$\n" + 
			")\n" + 
			"public class X {\n" + 
			"	public static void main(String[] args) {\n" + 
			"		if (false) {\n" + 
			"			;\n" + 
			"		} else {\n" + 
			"		}\n" + 
			"		Zork z;\n" + 
			"	}\n" + 
			"}"
        },
        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
        + " -1.5 -g -preserveAllLocals"
        + " -bootclasspath " + JRE_HOME_DIR + "/lib/rt.jar"
        + " -cp " + JRE_HOME_DIR + "/lib/jce.jar"
        + " -verbose -warn:+deprecation,syntheticAccess,uselessTypeCheck,unsafe,finalBound,unusedLocal"
        + " -proceedOnError -referenceInfo -d \"" + OUTPUT_DIR + "\"",
        "[1 .class file generated]\n", 
        "----------\n" + 
        "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
        " (at line 11)\n" + 
        "	Zork z;\n" + 
        "	^^^^\n" + 
        "Zork cannot be resolved to a type\n" + 
        "----------\n" + 
        "1 problem (1 error)", true);
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=92398 -- a case that works, another that does not
// revisit this test case depending on https://bugs.eclipse.org/bugs/show_bug.cgi?id=95349
public void _test009(){
	this.runNegativeTest(
		new String[] {
			"X.java",
			"/** */\n" + 
			"public class X {\n" + 
			"	OK1 ok1;\n" + 
			"	OK2 ok2;\n" + 
			"	Warn warn;\n" + 
			"	KO ko;\n" + 
	        "	Zork z;\n" + 
			"}",
			"OK1.java",
			"/** */\n" + 
			"public class OK1 {\n" + 
			"	// empty\n" + 
			"}",
			"OK2.java",
			"/** */\n" + 
			"public class OK2 {\n" + 
			"	// empty\n" + 
			"}",
			"Warn.java",
			"/** */\n" + 
			"public class Warn {\n" + 
			"	// empty\n" + 
			"}",
			"KO.java",
			"/** */\n" + 
			"public class KO {\n" + 
			"	// empty\n" + 
			"}",
		},
        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
        + " -1.5 -g -preserveAllLocals"
        + " -cp \"" + OUTPUT_DIR + "[+OK2" + File.pathSeparator + "~Warn" 
        	+ File.pathSeparator + "-KO]\""
        + " -verbose -warn:+deprecation,syntheticAccess,uselessTypeCheck,unsafe,finalBound,unusedLocal"
        + " -proceedOnError -referenceInfo -d \"" + OUTPUT_DIR + "\"",
        "[5 .class files generated]\n", 
        "----------\n" + 
        "1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
        " (at line 5)\n" + 
        "	Warn warn;\n" + 
        "	^^^^\n" + 
        "Discouraged access: Warn\n" + 
        "----------\n" + 
        "----------\n" + 
        "2. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
        " (at line 6)\n" + 
        "	KO ko;\n" + 
        "	^^\n" + 
        "Access restriction: KO\n" + 
        "----------\n" + 
        "----------\n" + 
        "3. ERROR in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
        " (at line 7)\n" + 
        "	Zork z;\n" + 
        "	^^^^\n" + 
        "Zork cannot be resolved to a type\n" + 
        "----------\n" + 
        "3 problems (1 error, 2 warnings)",
        true);
}
// command line - no user classpath nor bootclasspath
public void _test010(){
	this.runConformTest(
		new String[] {
			"X.java",
			"import java.util.List;\n" + 
			"\n" + 
			"@SuppressWarnings(\"all\"//$NON-NLS-1$\n" + 
			")\n" + 
			"public class X {\n" + 
			"	public static void main(String[] args) {\n" + 
			"		if (false) {\n" + 
			"			;\n" + 
			"		} else {\n" + 
			"		}\n" + 
			"		// Zork z;\n" + 
			"	}\n" + 
			"}"
        },
        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
        + " -1.5 -g -preserveAllLocals"
        + " -verbose -warn:+deprecation,syntheticAccess,uselessTypeCheck,unsafe,finalBound,unusedLocal"
        + " -proceedOnError -referenceInfo -d \"" + OUTPUT_DIR + "\"",
        "[1 .class file generated]\n", 
        "----------\n" + 
        "1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
        " (at line 1)\n" + 
        "	import java.util.List;\n" + 
        "	       ^^^^^^^^^^^^^^\n" + 
        "The import java.util.List is never used\n" + 
        "----------\n" + 
        "1 problem (1 warning)", true);
}
// command line - unusual classpath (ends with ';', still OK)
public void test011(){
	this.runConformTest(
		new String[] {
			"X.java",
			"/** */\n" + 
			"public class X {\n" + 
			"}",
		},
        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
        + " -1.5 -g -preserveAllLocals"
        + " -cp \"" + OUTPUT_DIR + "[+**/OK2;~**/Warn;-KO]"
        + "\"" + File.pathSeparator
        + " -proceedOnError -referenceInfo -d \"" + OUTPUT_DIR + "\"",
        "",
        "",
        true);
}
// command line - help
public void _test012(){
	final String expectedOutput =
        "{0}\n" + 
        " \n" + 
        " Usage: <options> <source files | directories>\n" + 
        " If directories are specified, then their source contents are compiled.\n" + 
        " Possible options are listed below. Options enabled by default are prefixed with \'\'+\'\'\n" + 
        " \n" + 
        " Classpath options:\n" + 
        "    -cp -classpath <directories and zip/jar files separated by {1}>\n" + 
        "                       specify location for application classes and sources. Each\n" + 
        "                       directory or file can specify access rules for types between\n" + 
        "                       \'\'[\'\' and \'\']\'\' (e.g. [-X.java] to deny access to type X)\n" + 
        "    -bootclasspath <directories and zip/jar files separated by {1}>\n" + 
        "                       specify location for system classes. Each directory or file can\n" + 
        "                       specify access rules for types between \'\'[\'\' and \'\']\'\' (e.g. [-X.java]\n" + 
        "                       to deny access to type X)\n" + 
        "    -sourcepath <directories and zip/jar files separated by {1}>\n" + 
        "                       specify location for application sources. Each directory or file can\n" + 
        "                       specify access rules for types between \'\'[\'\' and \'\']\'\' (e.g. [-X.java]\n" + 
        "                       to deny access to type X)\n" + 
        "    -extdirs <directories separated by {1}>\n" + 
        "                       specify location for extension zip/jar files\n" + 
        "    -d <dir>           destination directory (if omitted, no directory is created)\n" + 
        "    -d none            generate no .class files\n" + 
        "    -encoding <enc>    specify custom encoding for all sources. Each file/directory can override it\n" + 
        "                       when suffixed with \'\'[\'\'<enc>\'\']\'\' (e.g. X.java[utf8])\n" + 
        " \n" + 
        " Compliance options:\n" + 
        "    -1.3               use 1.3 compliance level (implicit -source 1.3 -target 1.1)\n" + 
        "    -1.4             + use 1.4 compliance level (implicit -source 1.3 -target 1.2)\n" + 
        "    -1.5               use 1.5 compliance level (implicit -source 1.5 -target 1.5)\n" + 
        "    -source <version>  set source level: 1.3 to 1.5 (or 5 or 5.0)\n" + 
        "    -target <version>  set classfile target level: 1.1 to 1.5 (or 5 or 5.0)\n" + 
        " \n" + 
        " Warning options:\n" + 
        "    -deprecation     + deprecation outside deprecated code\n" + 
        "    -nowarn            disable all warnings\n" + 
        "    -warn:none         disable all warnings\n" + 
        "    -warn:<warnings separated by ,>    enable exactly the listed warnings\n" + 
        "    -warn:+<warnings separated by ,>   enable additional warnings\n" + 
        "    -warn:-<warnings separated by ,>   disable specific warnings\n" + 
        "      allDeprecation       deprecation including inside deprecated code\n" + 
        "      allJavadoc           invalid or missing javadoc\n" + 
        "      assertIdentifier   + \'\'assert\'\' used as identifier\n" + 
        "      boxing               autoboxing conversion\n" + 
        "      charConcat         + char[] in String concat\n" + 
        "      conditionAssign      possible accidental boolean assignment\n" + 
        "      constructorName    + method with constructor name\n" + 
        "      dep-ann              missing @Deprecated annotation\n" + 
        "      deprecation        + deprecation outside deprecated code\n" + 
        "      emptyBlock           undocumented empty block\n" + 
        "      enumSwitch           incomplete enum switch\n" + 
        "      fieldHiding          field hiding another variable\n" + 
        "      finalBound           type parameter with final bound\n" + 
        "      finally            + finally block not completing normally\n" + 
        "      indirectStatic       indirect reference to static member\n" + 
        "      intfAnnotation     + annotation type used as super interface\n" + 
        "      intfNonInherited   + interface non-inherited method compatibility\n" + 
        "      javadoc              invalid javadoc\n" + 
        "      localHiding          local variable hiding another variable\n" + 
        "      maskedCatchBlock   + hidden catch block\n" + 
        "      nls                  string literal lacking non-nls tag //$NON-NLS-<n>$\n" + 
        "      noEffectAssign     + assignment without effect\n" + 
        "      null                 missing or redundant null check\n" + 
        "      over-ann             missing @Override annotation\n" + 
        "      pkgDefaultMethod   + attempt to override package-default method\n" + 
        "      semicolon            unnecessary semicolon, empty statement\n" + 
        "      serial             + missing serialVersionUID\n" + 
        "      suppress           + enable @SuppressWarnings\n" + 
        "      unqualifiedField     unqualified reference to field\n" + 
        "      unchecked          + unchecked type operation\n" + 
        "      unusedArgument       unread method parameter\n" + 
        "      unusedImport       + unused import declaration\n" + 
        "      unusedLocal          unread local variable\n" + 
        "      unusedPrivate        unused private member declaration\n" + 
        "      unusedThrown         unused declared thrown exception\n" + 
        "      unnecessaryElse      unnecessary else clause\n" + 
        "      uselessTypeCheck     unnecessary cast/instanceof operation\n" + 
        "      specialParamHiding   constructor or setter parameter hiding another field\n" + 
        "      staticReceiver     + non-static reference to static member\n" + 
        "      syntheticAccess      synthetic access for innerclass\n" + 
        "      tasks(<tags separated by |>) tasks identified by tags inside comments\n" + 
        "      typeHiding         + type parameter hiding another type\n" + 
        "      varargsCast        + varargs argument need explicit cast\n" + 
        "      warningToken       + unhandled warning token in @SuppressWarnings\n" + 
        " \n" + 
        " Debug options:\n" + 
        "    -g[:lines,vars,source] custom debug info\n" + 
        "    -g:lines,source  + both lines table and source debug info\n" + 
        "    -g                 all debug info\n" + 
        "    -g:none            no debug info\n" + 
        "    -preserveAllLocals preserve unused local vars for debug purpose\n" + 
        " \n" + 
        " Ignored options:\n" + 
        "    -J<option>         pass option to virtual machine (ignored)\n" + 
        "    -X<option>         specify non-standard option (ignored)\n" + 
        "    -X                 print non-standard options and exit (ignored)\n" + 
        "    -O                 optimize for execution time (ignored)\n" + 
        " \n" + 
        " Advanced options:\n" + 
        "    @<file>            read command line arguments from file\n" + 
        "    -maxProblems <n>   max number of problems per compilation unit (100 by default)\n" + 
        "    -log <file>        log to a file\n" + 
        "    -proceedOnError    do not stop at first error, dumping class files with problem methods\n" + 
        "    -verbose           enable verbose output\n" + 
        "    -referenceInfo     compute reference info\n" + 
        "    -progress          show progress (only in -log mode)\n" + 
        "    -time              display speed information \n" + 
        "    -noExit            do not call System.exit(n) at end of compilation (n==0 if no error)\n" + 
        "    -repeat <n>        repeat compilation process <n> times for perf analysis\n" + 
        "    -inlineJSR         inline JSR bytecode (implicit if target >= 1.5)\n" + 
        "    -enableJavadoc     consider references in javadoc\n" + 
        " \n" + 
        "    -? -help           print this help message\n" + 
        "    -v -version        print compiler version\n" + 
        "    -showversion       print compiler version and continue\n" + 
        "\n";

	this.runConformTest(
		new String[0],
        " -help -showversion -referenceInfo",
		MessageFormat.format(expectedOutput, new String[] {
				Main.bind("misc.version", new String[] {
					Main.bind("compiler.name"),
					Main.bind("compiler.version"),
					Main.bind("compiler.copyright")
				}),
				File.pathSeparator
		}),
        "", true);
}

	// command line - xml log contents https://bugs.eclipse.org/bugs/show_bug.cgi?id=93904
	public void _test013() {
		String logFileName = OUTPUT_DIR + File.separator + "log.xml";
		this.runNegativeTest(new String[] { 
				"X.java",
				"/** */\n" + 
				"public class X {\n" + 
				"	Zork z;\n" + 
				"}", },
				"\"" + OUTPUT_DIR + File.separator + "X.java\""
				+ " -1.5 -proceedOnError"
				+ " -log \"" + logFileName + "\" -d \"" + OUTPUT_DIR + "\"",
				"", 
				"----------\n" + 
				"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
				" (at line 3)\n" + 
				"	Zork z;\n" + 
				"	^^^^\n" + 
				"Zork cannot be resolved to a type\n" + 
				"----------\n" + 
				"1 problem (1 error)", 
				true);
		String logContents = Util.fileContent(logFileName);
		String expectedLogContents = 
			"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + 
			"<!DOCTYPE compiler SYSTEM \"compiler.dtd\">\n" + 
			"<compiler name=\"Eclipse Java Compiler\" copyright=\"Copyright IBM Corp 2000, 2005. All rights reserved.\" version=\"{1}\">\n" + 
			"	<command_line>\n" + 
			"		<argument value=\"---OUTPUT_DIR_PLACEHOLDER---{0}X.java\"/>\n" + 
			"		<argument value=\"-1.5\"/>\n" + 
			"		<argument value=\"-proceedOnError\"/>\n" + 
			"		<argument value=\"-log\"/>\n" + 
			"		<argument value=\"---OUTPUT_DIR_PLACEHOLDER---{0}log.xml\"/>\n" + 
			"		<argument value=\"-d\"/>\n" + 
			"		<argument value=\"---OUTPUT_DIR_PLACEHOLDER---\"/>\n" + 
			"	</command_line>\n" + 
			"	<options>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode\" value=\"disabled\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.codegen.targetPlatform\" value=\"1.5\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.codegen.unusedLocal\" value=\"optimize out\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.compliance\" value=\"1.5\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.debug.lineNumber\" value=\"generate\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.debug.localVariable\" value=\"do not generate\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.debug.sourceFile\" value=\"generate\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.doc.comment.support\" value=\"disabled\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.maxProblemPerUnit\" value=\"100\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.annotationSuperInterface\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.assertIdentifier\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.autoboxing\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.deprecation\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode\" value=\"disabled\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod\" value=\"disabled\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.discouragedReference\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.emptyStatement\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.enumIdentifier\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.fieldHiding\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.finalParameterBound\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.forbiddenReference\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.indirectStaticAccess\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.invalidJavadoc\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.invalidJavadocTags\" value=\"enabled\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef\" value=\"enabled\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef\" value=\"enabled\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility\" value=\"private\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.localVariableHiding\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.methodWithConstructorName\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.missingJavadocComments\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding\" value=\"disabled\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility\" value=\"public\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.missingJavadocTags\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding\" value=\"disabled\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility\" value=\"private\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.missingSerialVersion\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.noEffectAssignment\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.nullReference\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.specialParameterHidingField\" value=\"disabled\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.staticAccessReceiver\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.suppressWarnings\" value=\"enabled\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.typeParameterHiding\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unhandledWarningToken\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unnecessaryElse\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding\" value=\"disabled\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unusedImport\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unusedLocal\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unusedParameter\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract\" value=\"disabled\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete\" value=\"disabled\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unusedPrivateMember\" value=\"ignore\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast\" value=\"warning\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.source\" value=\"1.5\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.taskCaseSensitive\" value=\"enabled\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.taskPriorities\" value=\"\"/>\n" + 
			"		<option key=\"org.eclipse.jdt.core.compiler.taskTags\" value=\"\"/>\n" + 
			"	</options>\n" + 
			"	<classpaths>NORMALIZED SECTION</classpaths>\n" + 
			"	<sources>\n" + 
			"		<source path=\"---OUTPUT_DIR_PLACEHOLDER---{0}X.java\">\n" + 
			"			<problems problems=\"1\" errors=\"1\" warnings=\"0\">\n" + 
			"				<problem charEnd=\"28\" charStart=\"25\" severity=\"ERROR\" line=\"3\" id=\"UndefinedType\">\n" + 
			"					<message value=\"Zork cannot be resolved to a type\"/>\n" + 
			"					<source_context value=\"Zork z;\" sourceStart=\"0\" sourceEnd=\"3\"/>\n" + 
			"					<arguments>\n" + 
			"						<argument value=\"Zork\"/>\n" + 
			"					</arguments>\n" + 
			"				</problem>\n" + 
			"			</problems>\n" + 
			"			<classfile path=\"---OUTPUT_DIR_PLACEHOLDER---{0}X.class\"/>\n" + 
			"		</source>\n" + 
			"	</sources>\n" + 
			"	<stats>\n" + 
			"		<problem_summary problems=\"1\" errors=\"1\" warnings=\"0\" tasks=\"0\"/>\n" + 
			"	</stats>\n" + 
			"</compiler>\n";
		boolean compareOK = semiNormalizedComparison(
				MessageFormat.format(
						expectedLogContents,
						new String[] {
								File.separator,
								Main.bind("compiler.version")
						}),
				logContents, xmlLogsNormalizer);
		if (!compareOK) {
			System.out.println(getClass().getName() + '#' + getName());
			System.out.println(
					  "------------ [START LOG] ------------\n"
					+ "------------- Expected: -------------\n"
					+ expectedLogContents
				  + "\n------------- but was:  -------------\n"
					+ xmlLogsNormalizer.normalized(logContents)
				  + "\n--------- (cut and paste:) ----------\n"
					+ Util.displayString(xmlLogsNormalizer.normalized(logContents))
				  + "\n------------- [END LOG] -------------\n");
		}
		assertTrue("unexpected log contents", compareOK);
	}

	// command line - txt log contents https://bugs.eclipse.org/bugs/show_bug.cgi?id=93904
	public void test014() {
		String logFileName = OUTPUT_DIR + File.separator + "log.txt";
		this.runNegativeTest(new String[] { 
				"X.java",
				"/** */\n" + 
				"public class X {\n" + 
				"	Zork z;\n" + 
				"}", },
				"\"" + OUTPUT_DIR + File.separator + "X.java\"" 
				+ " -1.5 -proceedOnError"
				+ " -log \"" + logFileName + "\" -d \"" + OUTPUT_DIR + "\"",
				"", 
				"----------\n" + 
				"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
				" (at line 3)\n" + 
				"	Zork z;\n" + 
				"	^^^^\n" + 
				"Zork cannot be resolved to a type\n" + 
				"----------\n" + 
				"1 problem (1 error)", 
				false);
		String logContents = Util.fileContent(logFileName);
		String expectedLogContents = 
			"----------\n" + 
			"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
			" (at line 3)\n" + 
			"	Zork z;\n" + 
			"	^^^^\n" + 
			"Zork cannot be resolved to a type\n" + 
			"----------\n" + 
			"1 problem (1 error)";
		boolean compareOK = semiNormalizedComparison(expectedLogContents,
				logContents, textLogsNormalizer);
		if (!compareOK) {
			System.out.println(getClass().getName() + '#' + getName());
			System.out.println(
							  "------------ [START LOG] ------------\n"
							+ "------------- Expected: -------------\n"
							+ expectedLogContents
						  + "\n------------- but was:  -------------\n"
							+ outputDirNormalizer.normalized(logContents)
						  + "\n--------- (cut and paste:) ----------\n"
							+ Util.displayString(outputDirNormalizer.normalized(logContents))
						  + "\n------------- [END LOG] -------------\n");
		}
		assertTrue("unexpected log contents", compareOK);
	}

	// command line - no extension log contents https://bugs.eclipse.org/bugs/show_bug.cgi?id=93904
	public void test015() {
		String logFileName = OUTPUT_DIR + File.separator + "log";
		this.runNegativeTest(new String[] { 
				"X.java",
				"/** */\n" + 
				"public class X {\n" + 
				"	Zork z;\n" + 
				"}", },
				"\"" + OUTPUT_DIR + File.separator + "X.java\""
				+ " -1.5 -proceedOnError"
				+ " -log \"" + logFileName + "\" -d \"" + OUTPUT_DIR + "\"",
				"", 
				"----------\n" + 
				"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
				" (at line 3)\n" + 
				"	Zork z;\n" + 
				"	^^^^\n" + 
				"Zork cannot be resolved to a type\n" + 
				"----------\n" + 
				"1 problem (1 error)", 
				false);
		String logContents = Util.fileContent(logFileName);
		String expectedLogContents = 
			"----------\n" + 
			"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
			" (at line 3)\n" + 
			"	Zork z;\n" + 
			"	^^^^\n" + 
			"Zork cannot be resolved to a type\n" + 
			"----------\n" + 
			"1 problem (1 error)";
		boolean compareOK = semiNormalizedComparison(expectedLogContents,
				logContents, textLogsNormalizer);
		if (!compareOK) {
			System.out.println(getClass().getName() + '#' + getName());
			System.out.println(
					  "------------ [START LOG] ------------\n"
					+ "------------- Expected: -------------\n"
					+ expectedLogContents
				  + "\n------------- but was:  -------------\n"
					+ outputDirNormalizer.normalized(logContents)
				  + "\n--------- (cut and paste:) ----------\n"
					+ Util.displayString(outputDirNormalizer.normalized(logContents))
				  + "\n------------- [END LOG] -------------\n");
		}
		assertTrue("unexpected log contents", compareOK);
	}
public void _test016(){
		this.runConformTest(
			new String[] {
					"X.java",
					"/** */\n" + 
					"public class X {\n" + 
					"	OK1 ok1;\n" + 
					"}",
					"OK1.java",
					"/** */\n" + 
					"public class OK1 {\n" + 
					"	// empty\n" + 
					"}"
			},
	        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
	        + " -1.5 -g -preserveAllLocals"
	        + " -cp ." + File.pathSeparator + File.pathSeparator + File.pathSeparator + "\"" + OUTPUT_DIR + "\""
	        + " -verbose -proceedOnError -referenceInfo"
	        + " -d \"" + OUTPUT_DIR + "\"",
	        "[2 .class files generated]\n",
	        "",
	        true);
	}
public void _test017(){
		this.runConformTest(
			new String[] {
					"X.java",
					"/** */\n" + 
					"public class X {\n" + 
					"	OK1 ok1;\n" + 
					"}",
					"OK1.java",
					"/** */\n" + 
					"public class OK1 {\n" + 
					"	// empty\n" + 
					"}"
			},
	        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
	        + " -1.5 -g -preserveAllLocals"
	        + " -cp dummmy_dir" + File.pathSeparator + "dummy.jar" + File.pathSeparator + File.pathSeparator + "\"" + OUTPUT_DIR + "\"" 
	        + " -verbose -proceedOnError -referenceInfo" 
	        + " -d \"" + OUTPUT_DIR + "\"",
	        "[2 .class files generated]\n",
	        "incorrect classpath: dummmy_dir\n" + 
	        "incorrect classpath: dummy.jar\n" + 
	        "incorrect classpath: dummy.jar\n",
	        true);
	}
// command line - unusual classpath (empty, but using current directory, still OK provided 
//	that we execute from the appropriate directory)
	public void _test018(){
		this.runConformTest(
			new String[] {
					"X.java",
					"/** */\n" + 
					"public class X {\n" + 
					"	OK1 ok1;\n" + 
					"}",
					"OK1.java",
					"/** */\n" + 
					"public class OK1 {\n" + 
					"	// empty\n" + 
					"}"
			},
	        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
	        + " -1.5 -g -preserveAllLocals"
	        + " -verbose -proceedOnError -referenceInfo"
	        + " -d \"" + OUTPUT_DIR + "\"",
	        "[2 .class files generated]\n",
	        "",
	        true);
	}
public void _test019(){
		this.runNegativeTest(
			new String[] {
				"X.java",
				"/** */\n" + 
				"public class X {\n" + 
				"	OK1 ok1;\n" + 
				"	OK2 ok2;\n" + 
				"	Warn warn;\n" + 
				"	KO ko;\n" + 
		        "	Zork z;\n" + 
				"}",
				"OK1.java",
				"/** */\n" + 
				"public class OK1 {\n" + 
				"	// empty\n" + 
				"}",
				"OK2.java",
				"/** */\n" + 
				"public class OK2 {\n" + 
				"	// empty\n" + 
				"}",
				"Warn.java",
				"/** */\n" + 
				"public class Warn {\n" + 
				"	// empty\n" + 
				"}",
				"KO.java",
				"/** */\n" + 
				"public class KO {\n" + 
				"	// empty\n" + 
				"}",
			},
	        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
	        + " -1.5 -g -preserveAllLocals" 
	        + " -cp \"" + OUTPUT_DIR + "[+OK2" + File.pathSeparator + "~Warn" + File.pathSeparator + "-KO]\""
	        + " -verbose -warn:+deprecation,syntheticAccess,uselessTypeCheck,unsafe,finalBound,unusedLocal" 
	        + " -proceedOnError -referenceInfo"
	        + " -d \"" + OUTPUT_DIR + "\"", 
	        "[5 .class files generated]\n", 
	        "----------\n" + 
	        "1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
	        " (at line 5)\n" + 
	        "	Warn warn;\n" + 
	        "	^^^^\n" + 
	        "Discouraged access: Warn\n" + 
	        "----------\n" + 
	        "----------\n" + 
	        "2. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
	        " (at line 6)\n" + 
	        "	KO ko;\n" + 
	        "	^^\n" + 
	        "Access restriction: KO\n" + 
	        "----------\n" + 
	        "----------\n" + 
	        "3. ERROR in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
	        " (at line 7)\n" + 
	        "	Zork z;\n" + 
	        "	^^^^\n" + 
	        "Zork cannot be resolved to a type\n" + 
	        "----------\n" + 
	        "3 problems (1 error, 2 warnings)",
	        true);
	}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=88364 - skip options -O -Jxxx and -Xxxx, multiple times if needed
	public void _test020(){
		this.runConformTest(
			new String[] {
					"X.java",
					"/** */\n" + 
					"public class X {\n" + 
					"}",
			},
	        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
	        + " -1.5 -g -preserveAllLocals"
	        + " -verbose -proceedOnError -referenceInfo"
	        + " -d \"" + OUTPUT_DIR + "\" -O -Xxxx -O -Jxyz -Xtyu -Jyu",
	        "[1 .class file generated]\n",
	        "",
	        true);
	}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=88364 - -sourcepath finds additional source files
	public void _test021(){
		this.runConformTest(
			new String[] {
					"src1/X.java",
					"/** */\n" + 
					"public class X {\n" + 
					"}",
					"src2/Y.java",
					"/** */\n" + 
					"public class Y extends X {\n" + 
					"}",
			},
	        "\"" + OUTPUT_DIR +  File.separator + "src2/Y.java\""
			+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src1\"" 
			  + File.pathSeparator + "\"" + OUTPUT_DIR +  File.separator + "src2\""
	        + " -1.5 -g -preserveAllLocals"
	        + " -verbose -proceedOnError -referenceInfo"
	        + " -d \"" + OUTPUT_DIR + "\" ",
	        "[2 .class files generated]\n",
	        "",
	        true);
	}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=88364 - repeated -sourcepath fails - even if the error is more
// explicit here than what javac does
	public void test022(){
		this.runNegativeTest(
			new String[] {
					"src1/X.java",
					"/** */\n" + 
					"public class X {\n" + 
					"}",
					"src2/Y.java",
					"/** */\n" + 
					"public class Y extends X {\n" + 
					"}",
			},
			" -sourcepath \"" + OUTPUT_DIR +  File.separator + "src1\"" 
			+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src2\"" 
	        + " \"" + OUTPUT_DIR +  File.separator + "src2/Y.java\""
	        + " -1.5 -g -preserveAllLocals"
	        + " -verbose -proceedOnError -referenceInfo"
	        + " -d \"" + OUTPUT_DIR + "\" ",
	        "",
	        "duplicate sourcepath specification: -sourcepath\n",
	        true);
	}
//	 https://bugs.eclipse.org/bugs/show_bug.cgi?id=88364 - repeated -extdirs fails
		public void test023(){
			this.runNegativeTest(
				new String[] {
						"src1/X.java",
						"/** */\n" + 
						"public class X {\n" + 
						"}",
						"src2/Y.java",
						"/** */\n" + 
						"public class Y extends X {\n" + 
						"}",
				},
				" -extdirs \"" + OUTPUT_DIR +  File.separator + "src1\"" 
				+ " -extdirs \"" + OUTPUT_DIR +  File.separator + "src2\"" 
		        + " \"" + OUTPUT_DIR +  File.separator + "src1" + File.separator + "X.java\""
		        + " -1.5 -g -preserveAllLocals"
		        + " -verbose -proceedOnError -referenceInfo"
		        + " -d \"" + OUTPUT_DIR + "\" ",
		        "",
		        "duplicate extdirs specification: -extdirs\n",
		        true);
		}
//	 https://bugs.eclipse.org/bugs/show_bug.cgi?id=88364 - explicit empty -extdirs removes extensions
		public void _test024(){
			this.runNegativeTest(
				new String[] {
						"X.java",
						"/** */\n" + 
						"public class X {\n" + 
						"  sun.net.spi.nameservice.dns.DNSNameService dummy;\n" + 
						"}",
				},
		        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
				+ " -extdirs \"\"" 
		        + " -1.5 -g -preserveAllLocals"
		        + " -verbose -proceedOnError -referenceInfo"
		        + " -d \"" + OUTPUT_DIR + "\" ",
		        "[1 .class file generated]\n",
		        "----------\n" + 
		        "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
		        " (at line 3)\n" + 
		        "	sun.net.spi.nameservice.dns.DNSNameService dummy;\n" + 
		        "	^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + 
		        "sun.net.spi.nameservice.dns cannot be resolved to a type\n" + 
		        "----------\n" + 
		        "1 problem (1 error)",
		        true);
		}
//	 https://bugs.eclipse.org/bugs/show_bug.cgi?id=88364 - cumulative -extdirs extends the classpath
		public void _test025(){
			this.runConformTest(
				new String[] {
						"src1/X.java",
						"/** */\n" + 
						"public class X {\n" + 
						"  sun.net.spi.nameservice.dns.DNSNameService dummy;\n" + 
						"}",
						"src2/Y.java",
						"/** */\n" + 
						"public class Y extends X {\n" + 
						"}",
				},
		        "\"" + OUTPUT_DIR +  File.separator + "src2/Y.java\""
				+ " -extdirs \"" + JRE_HOME_DIR + "/lib/ext" + File.pathSeparator + OUTPUT_DIR +  File.separator + "src1\"" 
				+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src1\"" 
		        + " -1.5 -g -preserveAllLocals"
		        + " -verbose -proceedOnError -referenceInfo"
		        + " -d \"" + OUTPUT_DIR + "\" ",
		        "[2 .class files generated]\n",
		        "",
		        true);
		}
//	 https://bugs.eclipse.org/bugs/show_bug.cgi?id=88364 - -extdirs extends the classpath before -classpath
		public void _test026(){
			this.runConformTest(
				new String[] {
						"src1/X.java",
						"/** */\n" + 
						"public class X {\n" + 
						"}",
						"src2/Y.java",
						"/** */\n" + 
						"public class Y extends X {\n" + 
						"}",
						"src3/X.java",
						"/** */\n" + 
						"public class X {\n" + 
						"  Zork error;\n" + 
						"}",
				},
		        "\"" + OUTPUT_DIR +  File.separator + "src2/Y.java\""
				+ " -classpath \"" + OUTPUT_DIR +  File.separator + "src3\"" 
				+ " -extdirs \"" + JRE_HOME_DIR + "/lib/ext" + File.pathSeparator + OUTPUT_DIR +  File.separator + "src1\"" 
				+ " -sourcepath \"" + OUTPUT_DIR +  File.separator + "src2" + File.pathSeparator + OUTPUT_DIR +  File.separator + "src1\"" 
		        + " -1.5 -g -preserveAllLocals"
		        + " -verbose -proceedOnError -referenceInfo"
		        + " -d \"" + OUTPUT_DIR + "\" ",
		        "[2 .class files generated]\n",
				"",
		        true);
		}
		
public void _test027(){
			this.runNegativeTest(
				new String[] {
					"X.java",
					"/** */\n" + 
					"public class X {\n" + 
					"	OK1 ok1;\n" + 
					"	OK2 ok2;\n" + 
					"	Warn warn;\n" + 
					"	KO ko;\n" + 
			        "	Zork z;\n" + 
					"}",
					"OK1.java",
					"/** */\n" + 
					"public class OK1 {\n" + 
					"	// empty\n" + 
					"}",
					"OK2.java",
					"/** */\n" + 
					"public class OK2 {\n" + 
					"	// empty\n" + 
					"}",
					"p1/Warn.java",
					"/** */\n" + 
					"public class Warn {\n" + 
					"	// empty\n" + 
					"}",
					"KO.java",
					"/** */\n" + 
					"public class KO {\n" + 
					"	// empty\n" + 
					"}",
				},
		        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
		        + " -1.5 -g -preserveAllLocals"
		        + " -cp \"" + OUTPUT_DIR + "[+OK2" + File.pathSeparator + "-KO]" + File.pathSeparator
		        + OUTPUT_DIR + File.separator + "p1[~Warn]\""
		        + " -verbose -warn:+deprecation,syntheticAccess,uselessTypeCheck,unsafe,finalBound,unusedLocal"
		        + " -proceedOnError -referenceInfo -d \"" + OUTPUT_DIR + "\"",
		        "[5 .class files generated]\n", 
		        "----------\n" + 
		        "1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
		        " (at line 5)\n" + 
		        "	Warn warn;\n" + 
		        "	^^^^\n" + 
		        "Discouraged access: Warn\n" + 
		        "----------\n" + 
		        "----------\n" + 
		        "2. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
		        " (at line 6)\n" + 
		        "	KO ko;\n" + 
		        "	^^\n" + 
		        "Access restriction: KO\n" + 
		        "----------\n" + 
		        "----------\n" + 
		        "3. ERROR in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "X.java\n" + 
		        " (at line 7)\n" + 
		        "	Zork z;\n" + 
		        "	^^^^\n" + 
		        "Zork cannot be resolved to a type\n" + 
		        "----------\n" + 
		        "3 problems (1 error, 2 warnings)",
		        true);
		}
public void test028(){
			this.runConformTest(
				new String[] {
					"src1/X.java",
					"/** */\n" + 
					"public class X {\n" + 
					"}",
				},
		        "\"" + OUTPUT_DIR +  File.separator + "src1/X.java\""
		        + " -1.5 -g -preserveAllLocals"
		        + " -proceedOnError -referenceInfo" 
		        + " -d \"" + OUTPUT_DIR + File.separator + "bin/\"",
		        "",
		        "",
		        true);
			this.runConformTest(
				new String[] {
					"src2/Y.java",
					"/** */\n" + 
					"public class Y extends X {\n" + 
					"}",
				},
		        "\"" + OUTPUT_DIR +  File.separator + "src2/Y.java\""
		        + " -1.5 -g -preserveAllLocals"
		        + " -cp dummy" + File.pathSeparator + "\"" + OUTPUT_DIR + File.separator + "bin\"" + File.pathSeparator + "dummy"
		        + " -proceedOnError -referenceInfo"
		        + " -d \"" + OUTPUT_DIR + File.separator + "bin/\"",
		        "",
		        "incorrect classpath: dummy\n" + 
		        "incorrect classpath: dummy\n",
		        false);
		}
//Extraneous auto-build error message - https://bugs.eclipse.org/bugs/show_bug.cgi?id=93377
public void test030(){
	// first series shows that a clean build is OK
	this.runConformTest(
		new String[] {
			"X.java",
			"public interface X<T extends X<T, K, S>, \n" + 
			"                   K extends X.K<T, S>, \n" + 
			"                   S extends X.S> {\n" + 
			"	public interface K<KT extends X<KT, ?, KS>, \n" + 
			"	                   KS extends X.S> {\n" + 
			"	}\n" + 
			"	public interface S {\n" + 
			"	}\n" + 
			"}\n",
			"Y.java",
			"public class Y<T extends X<T, K, S>, \n" + 
			"               K extends X.K<T, S>, \n" + 
			"               S extends X.S> { \n" + 
			"}\n",
		},
        "\"" + OUTPUT_DIR +  File.separator + "Y.java\""
        + " -1.5 -g -preserveAllLocals"
        + " -cp \"" + OUTPUT_DIR + File.separator + "\""
        + " -proceedOnError -referenceInfo"
        + " -d \"" + OUTPUT_DIR + "\"",
        "",
        "",
        true);
	// second series shows that a staged build - that simulates the auto build context - is OK as well
	this.runConformTest(
		new String[] {
			"X.java",
			"public interface X<T extends X<T, K, S>, \n" + 
			"                   K extends X.K<T, S>, \n" + 
			"                   S extends X.S> {\n" + 
			"	public interface K<KT extends X<KT, ?, KS>, \n" + 
			"	                   KS extends X.S> {\n" + 
			"	}\n" + 
			"	public interface S {\n" + 
			"	}\n" + 
			"}\n",
		},
        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
        + " -1.5 -g -preserveAllLocals"
        + " -proceedOnError -referenceInfo" 
        + " -d \"" + OUTPUT_DIR + "\"",
        "",
        "",
        true);
	this.runConformTest(
		new String[] {
			"Y.java",
			"public class Y<T extends X<T, K, S>, \n" + 
			"               K extends X.K<T, S>, \n" + 
			"               S extends X.S> { \n" + 
			"}\n",
		},
        "\"" + OUTPUT_DIR +  File.separator + "Y.java\""
        + " -1.5 -g -preserveAllLocals"
        + " -cp \"" + OUTPUT_DIR + File.separator + "\""
        + " -proceedOnError -referenceInfo"
        + " -d \"" + OUTPUT_DIR + "\"",
        "",
        "",
        false);
}
// Extraneous auto-build error message - https://bugs.eclipse.org/bugs/show_bug.cgi?id=93377
// More complex test case than test30
public void test032(){
	// first series shows that a clean build is OK (warning messages only)
	this.runConformTest(
			new String[] {
				"p/X.java",
				"package p;\n" + 
				"import java.io.Serializable;\n" + 
				"public interface X<T extends X<T, U, V>, \n" + 
				"				   U extends X.XX<T, V>, \n" + 
				"				   V extends X.XY> {\n" + 
				"	public interface XX<TT extends X<TT, ?, UU>, \n" + 
				"	                    UU extends X.XY> \n" + 
				"			extends	Serializable {\n" + 
				"	}\n" + 
				"	public interface XY extends Serializable {\n" + 
				"	}\n" + 
				"}\n",
				"p/Y.java",
				"package p;\n" + 
				"import java.util.*;\n" + 
				"import p.X.*;\n" + 
				"public class Y<T extends X<T, U, V>, \n" + 
				"               U extends X.XX<T, V>, \n" + 
				"               V extends X.XY> {\n" + 
				"	private final Map<U, V> m1 = new HashMap<U, V>();\n" + 
				"	private final Map<U, T> m2 = new HashMap<U, T>();\n" + 
				"	private final Z m3;\n" + 
				"\n" + 
				"	public Y(final Z p1) {\n" + 
				"		this.m3 = p1;\n" + 
				"	}\n" + 
				"\n" + 
				"	public void foo1(final U p1, final V p2, final T p3) {\n" + 
				"		m1.put(p1, p2);\n" + 
				"		m2.put(p1, p3);\n" + 
				"		m3.foo2(p1, p2);\n" + 
				"	}\n" + 
				"\n" + 
				"	public void foo3(final U p1) {\n" + 
				"		assert m1.containsKey(p1);\n" + 
				"		m1.remove(p1);\n" + 
				"		m2.remove(p1);\n" + 
				"		m3.foo2(p1, null);\n" + 
				"	}\n" + 
				"\n" + 
				"	public Collection<T> foo4() {\n" + 
				"		return Collections.unmodifiableCollection(m2.values());\n" + 
				"	}\n" + 
				"\n" + 
				"	public void foo5(final Map<XX<?, ?>, XY> p1) {\n" + 
				"		p1.putAll(m1);\n" + 
				"	}\n" + 
				"\n" + 
				"	public void foo6(final Map<XX<?, ?>, XY> p1) {\n" + 
				"		m1.keySet().retainAll(p1.keySet());\n" + 
				"		m2.keySet().retainAll(p1.keySet());\n" + 
				"	}\n" + 
				"}\n",
				"p/Z.java",
				"package p;\n" + 
				"\n" + 
				"import java.util.*;\n" + 
				"\n" + 
				"import p.X.*;\n" + 
				"\n" + 
				"public class Z {\n" + 
				"	private final Map<Class<? extends X>, \n" + 
				"		              Y<?, ? extends XX<?, ?>, ? extends XY>> \n" + 
				"		m1 = new HashMap<Class<? extends X>, \n" + 
				"		                 Y<?, ? extends XX<?, ?>, ? extends XY>>();\n" + 
				"\n" + 
				"	private Map<X.XX<?, XY>, \n" + 
				"	            X.XY> \n" + 
				"		m2 = new HashMap<X.XX<?, XY>, \n" + 
				"		                 X.XY>();\n" + 
				"\n" + 
				"	public <T extends X<T, U, V>, \n" + 
				"	        U extends X.XX<T, V>, \n" + 
				"	        V extends X.XY> \n" + 
				"	Y<T, U, V> foo1(final Class<T> p1) {\n" + 
				"		Y l1 = m1.get(p1);\n" + 
				"		if (l1 == null) {\n" + 
				"			l1 = new Y<T, U, V>(this);\n" + 
				"			m1.put(p1, l1);\n" + 
				"		}\n" + 
				"		return l1;\n" + 
				"	}\n" + 
				"\n" + 
				"	public <TT extends X.XX<?, UU>, \n" + 
				"	        UU extends X.XY> \n" + 
				"	void foo2(final TT p1, final UU p2) {\n" + 
				"		m2.put((XX<?, XY>) p1, p2);\n" + 
				"	}\n" + 
				"\n" + 
				"	public Map<XX<?, ?>, XY> foo3() {\n" + 
				"		final Map<XX<?, ?>, \n" + 
				"		          XY> l1 = new HashMap<XX<?, ?>, \n" + 
				"		                               XY>();\n" + 
				"		for (final Y<?, \n" + 
				"				     ? extends XX<?, ?>, \n" + 
				"				     ? extends XY> \n" + 
				"				i : m1.values()) {\n" + 
				"			i.foo5(l1);\n" + 
				"		}\n" + 
				"		return l1;\n" + 
				"	}\n" + 
				"\n" + 
				"	public void foo4(final Object p1, final Map<XX<?, ?>, \n" + 
				"			                                    XY> p2) {\n" + 
				"		for (final Y<?, \n" + 
				"				     ? extends XX<?, ?>, \n" + 
				"				     ? extends XY> i : m1.values()) {\n" + 
				"			i.foo6(p2);\n" + 
				"		}\n" + 
				"		for (final Map.Entry<XX<?, ?>, \n" + 
				"				             XY> i : p2.entrySet()) {\n" + 
				"			final XX<?, XY> l1 = (XX<?, XY>) i.getKey();\n" + 
				"		}\n" + 
				"	}\n" + 
				"}\n"
			},
	        "\"" + OUTPUT_DIR +  File.separator + "p/X.java\""
	        + " \"" + OUTPUT_DIR +  File.separator + "p/Y.java\""
	        + " \"" + OUTPUT_DIR +  File.separator + "p/Z.java\""
	        + " -1.5 -g -preserveAllLocals"
	        + " -cp \"" + OUTPUT_DIR + File.separator + "\""
	        + " -proceedOnError -referenceInfo"
	        + " -d \"" + OUTPUT_DIR + "\"",
	        "",
	        "----------\n" + 
	        "1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "p" + File.separator + "Z.java\n" + 
	        " (at line 25)\n" + 
	        "	m1.put(p1, l1);\n" + 
	        "	           ^^\n" + 
	        "Type safety: The expression of type Y needs unchecked conversion to conform to Y<?,? extends X.XX<?,?>,? extends X.XY>\n" + 
	        "----------\n" + 
	        "----------\n" + 
	        "2. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "p" + File.separator + "Z.java\n" + 
	        " (at line 27)\n" + 
	        "	return l1;\n" + 
	        "	       ^^\n" + 
	        "Type safety: The expression of type Y needs unchecked conversion to conform to Y<T,U,V>\n" + 
	        "----------\n" + 
	        "----------\n" + 
	        "3. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "p" + File.separator + "Z.java\n" + 
	        " (at line 33)\n" + 
	        "	m2.put((XX<?, XY>) p1, p2);\n" + 
	        "	       ^^^^^^^^^^^^^^\n" + 
	        "Type safety: The cast from TT to X.XX<?,X.XY> is actually checking against the erased type X<T,U,V>.XX\n" + 
	        "----------\n" + 
	        "----------\n" + 
	        "4. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "p" + File.separator + "Z.java\n" + 
	        " (at line 58)\n" + 
	        "	final XX<?, XY> l1 = (XX<?, XY>) i.getKey();\n" + 
	        "	                ^^\n" + 
	        "The local variable l1 is never read\n" + 
	        "----------\n" + 
	        "----------\n" + 
	        "5. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "p" + File.separator + "Z.java\n" + 
	        " (at line 58)\n" + 
	        "	final XX<?, XY> l1 = (XX<?, XY>) i.getKey();\n" + 
	        "	                     ^^^^^^^^^^^^^^^^^^^^^^\n" + 
	        "Type safety: The cast from X.XX<capture-of ?,capture-of ?> to X.XX<?,X.XY> is actually checking against the erased type X<T,U,V>.XX\n" + 
	        "----------\n" + 
	        "5 problems (5 warnings)",
	        true);
	// second series shows that a staged build - that simulates the auto build context - is OK as well
	this.runConformTest(
		new String[] {
			"p/X.java",
			"package p;\n" + 
			"import java.io.Serializable;\n" + 
			"public interface X<T extends X<T, U, V>, \n" + 
			"				   U extends X.XX<T, V>, \n" + 
			"				   V extends X.XY> {\n" + 
			"	public interface XX<TT extends X<TT, ?, UU>, \n" + 
			"	                    UU extends X.XY> \n" + 
			"			extends	Serializable {\n" + 
			"	}\n" + 
			"	public interface XY extends Serializable {\n" + 
			"	}\n" + 
			"}\n",
		},
        "\"" + OUTPUT_DIR +  File.separator + "p/X.java\""
        + " -1.5 -g -preserveAllLocals"
        + " -proceedOnError -referenceInfo" 
        + " -d \"" + OUTPUT_DIR + "\"",
        "",
        "",
        true);
	this.runConformTest(
		new String[] {
			"p/Y.java",
			"package p;\n" + 
			"import java.util.*;\n" + 
			"import p.X.*;\n" + 
			"public class Y<T extends X<T, U, V>, \n" + 
			"               U extends X.XX<T, V>, \n" + 
			"               V extends X.XY> {\n" + 
			"	private final Map<U, V> m1 = new HashMap<U, V>();\n" + 
			"	private final Map<U, T> m2 = new HashMap<U, T>();\n" + 
			"	private final Z m3;\n" + 
			"\n" + 
			"	public Y(final Z p1) {\n" + 
			"		this.m3 = p1;\n" + 
			"	}\n" + 
			"\n" + 
			"	public void foo1(final U p1, final V p2, final T p3) {\n" + 
			"		m1.put(p1, p2);\n" + 
			"		m2.put(p1, p3);\n" + 
			"		m3.foo2(p1, p2);\n" + 
			"	}\n" + 
			"\n" + 
			"	public void foo3(final U p1) {\n" + 
			"		assert m1.containsKey(p1);\n" + 
			"		m1.remove(p1);\n" + 
			"		m2.remove(p1);\n" + 
			"		m3.foo2(p1, null);\n" + 
			"	}\n" + 
			"\n" + 
			"	public Collection<T> foo4() {\n" + 
			"		return Collections.unmodifiableCollection(m2.values());\n" + 
			"	}\n" + 
			"\n" + 
			"	public void foo5(final Map<XX<?, ?>, XY> p1) {\n" + 
			"		p1.putAll(m1);\n" + 
			"	}\n" + 
			"\n" + 
			"	public void foo6(final Map<XX<?, ?>, XY> p1) {\n" + 
			"		m1.keySet().retainAll(p1.keySet());\n" + 
			"		m2.keySet().retainAll(p1.keySet());\n" + 
			"	}\n" + 
			"}\n",
			"p/Z.java",
			"package p;\n" + 
			"\n" + 
			"import java.util.*;\n" + 
			"\n" + 
			"import p.X.*;\n" + 
			"\n" + 
			"public class Z {\n" + 
			"	private final Map<Class<? extends X>, \n" + 
			"		              Y<?, ? extends XX<?, ?>, ? extends XY>> \n" + 
			"		m1 = new HashMap<Class<? extends X>, \n" + 
			"		                 Y<?, ? extends XX<?, ?>, ? extends XY>>();\n" + 
			"\n" + 
			"	private Map<X.XX<?, XY>, \n" + 
			"	            X.XY> \n" + 
			"		m2 = new HashMap<X.XX<?, XY>, \n" + 
			"		                 X.XY>();\n" + 
			"\n" + 
			"	public <T extends X<T, U, V>, \n" + 
			"	        U extends X.XX<T, V>, \n" + 
			"	        V extends X.XY> \n" + 
			"	Y<T, U, V> foo1(final Class<T> p1) {\n" + 
			"		Y l1 = m1.get(p1);\n" + 
			"		if (l1 == null) {\n" + 
			"			l1 = new Y<T, U, V>(this);\n" + 
			"			m1.put(p1, l1);\n" + 
			"		}\n" + 
			"		return l1;\n" + 
			"	}\n" + 
			"\n" + 
			"	public <TT extends X.XX<?, UU>, \n" + 
			"	        UU extends X.XY> \n" + 
			"	void foo2(final TT p1, final UU p2) {\n" + 
			"		m2.put((XX<?, XY>) p1, p2);\n" + 
			"	}\n" + 
			"\n" + 
			"	public Map<XX<?, ?>, XY> foo3() {\n" + 
			"		final Map<XX<?, ?>, \n" + 
			"		          XY> l1 = new HashMap<XX<?, ?>, \n" + 
			"		                               XY>();\n" + 
			"		for (final Y<?, \n" + 
			"				     ? extends XX<?, ?>, \n" + 
			"				     ? extends XY> \n" + 
			"				i : m1.values()) {\n" + 
			"			i.foo5(l1);\n" + 
			"		}\n" + 
			"		return l1;\n" + 
			"	}\n" + 
			"\n" + 
			"	public void foo4(final Object p1, final Map<XX<?, ?>, \n" + 
			"			                                    XY> p2) {\n" + 
			"		for (final Y<?, \n" + 
			"				     ? extends XX<?, ?>, \n" + 
			"				     ? extends XY> i : m1.values()) {\n" + 
			"			i.foo6(p2);\n" + 
			"		}\n" + 
			"		for (final Map.Entry<XX<?, ?>, \n" + 
			"				             XY> i : p2.entrySet()) {\n" + 
			"			final XX<?, XY> l1 = (XX<?, XY>) i.getKey();\n" + 
			"		}\n" + 
			"	}\n" + 
			"}\n"
		},
        "\"" + OUTPUT_DIR +  File.separator + "p/Y.java\""
        + " \"" + OUTPUT_DIR +  File.separator + "p/Z.java\""
        + " -1.5 -g -preserveAllLocals"
        + " -cp \"" + OUTPUT_DIR + File.separator + "\""
        + " -proceedOnError -referenceInfo"
        + " -d \"" + OUTPUT_DIR + "\"",
        "",
        "----------\n" + 
        "1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "p" + File.separator + "Z.java\n" + 
        " (at line 25)\n" + 
        "	m1.put(p1, l1);\n" + 
        "	           ^^\n" + 
        "Type safety: The expression of type Y needs unchecked conversion to conform to Y<?,? extends X.XX<?,?>,? extends X.XY>\n" + 
        "----------\n" + 
        "----------\n" + 
        "2. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "p" + File.separator + "Z.java\n" + 
        " (at line 27)\n" + 
        "	return l1;\n" + 
        "	       ^^\n" + 
        "Type safety: The expression of type Y needs unchecked conversion to conform to Y<T,U,V>\n" + 
        "----------\n" + 
        "----------\n" + 
        "3. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "p" + File.separator + "Z.java\n" + 
        " (at line 33)\n" + 
        "	m2.put((XX<?, XY>) p1, p2);\n" + 
        "	       ^^^^^^^^^^^^^^\n" + 
        "Type safety: The cast from TT to X.XX<?,X.XY> is actually checking against the erased type X<T,U,V>.XX\n" + 
        "----------\n" + 
        "----------\n" + 
        "4. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "p" + File.separator + "Z.java\n" + 
        " (at line 58)\n" + 
        "	final XX<?, XY> l1 = (XX<?, XY>) i.getKey();\n" + 
        "	                ^^\n" + 
        "The local variable l1 is never read\n" + 
        "----------\n" + 
        "----------\n" + 
        "5. WARNING in ---OUTPUT_DIR_PLACEHOLDER---" + File.separator + "p" + File.separator + "Z.java\n" + 
        " (at line 58)\n" + 
        "	final XX<?, XY> l1 = (XX<?, XY>) i.getKey();\n" + 
        "	                     ^^^^^^^^^^^^^^^^^^^^^^\n" + 
        "Type safety: The cast from X.XX<capture-of ?,capture-of ?> to X.XX<?,X.XY> is actually checking against the erased type X<T,U,V>.XX\n" + 
        "----------\n" + 
        "5 problems (5 warnings)",
        false);
}
//https://bugs.eclipse.org/bugs/show_bug.cgi?id=104664
public void test033(){
	this.runConformTest(
		new String[] {
			"X.java",
			"/** */\n" + 
			"public class X {\n" + 
			"}",
		},
        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
        + " -1.5 -g -preserveAllLocals"
        + " -cp \"" + OUTPUT_DIR
        + "\"" + File.pathSeparator
        + " -repeat 2 -proceedOnError -referenceInfo -d \"" + OUTPUT_DIR + "\"",
        "[repetition 1/2]\n" + 
        "[repetition 2/2]\n",
        "",
        true);
}
//https://bugs.eclipse.org/bugs/show_bug.cgi?id=105430
public void test034(){
	this.runConformTest(
		new String[] {
			"X.java",
			"/** */\n" + 
			"public class X {\n" + 
			"}",
		},
        "\"" + OUTPUT_DIR +  File.separator + "X.java\""
        + " -1.5 -g -preserveAllLocals"
        + " -cp " + File.pathSeparator + "\"" + OUTPUT_DIR
        + "\"" + File.pathSeparator
        + " -proceedOnError -referenceInfo -d \"" + OUTPUT_DIR + "\"",
        "",
        "",
        true);
}
public static Class testClass() {
	return BatchCompilerTest.class;
}
}
