/*******************************************************************************
 * Copyright (c) 2000, 2012 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.util;

import org.eclipse.jdt.core.compiler.batch.BatchCompiler;
import org.eclipse.jdt.core.tests.runtime.*;
import java.io.*;
import java.net.*;
/**
 * Verifies that the .class files resulting from a compilation can be loaded
 * in a VM and that they can be run.
 */
public class TestVerifier {
	public String failureReason;

	boolean reuseVM = true;
	String[] classpathCache;
	LocalVirtualMachine vm;
	StringBuffer outputBuffer;
	StringBuffer errorBuffer;
	Socket socket;
public TestVerifier(boolean reuseVM) {
	this.reuseVM = reuseVM;
}
private boolean checkBuffers(String outputString, String errorString,
		String sourceFileName, String expectedOutputString, String expectedErrorStringStart) {
	boolean didMatchExpectation = true;
	String platformIndependantString;
	this.failureReason = null;
	if (expectedOutputString != null) {
		platformIndependantString = Util.convertToIndependantLineDelimiter(outputString.trim());
		if (!Util.convertToIndependantLineDelimiter(expectedOutputString).equals(platformIndependantString)) {
			System.out.println(Util.displayString(platformIndependantString, 2));
			this.failureReason =
				"Unexpected output running resulting class file for "
					+ sourceFileName
					+ ":\n"
					+ "--[START]--\n"
					+ outputString
					+ "---[END]---\n";
			didMatchExpectation = false;
		}
	}
	String trimmedErrorString = errorString.trim();
	if (expectedErrorStringStart != null) {
		platformIndependantString = Util.convertToIndependantLineDelimiter(trimmedErrorString);
		if (expectedErrorStringStart.length() == 0 && platformIndependantString.length() > 0 ||
				!platformIndependantString.startsWith(Util.convertToIndependantLineDelimiter(expectedErrorStringStart))) {
			/*
			 * This is an opportunistic heuristic for error strings comparison:
			 * - null means skip test;
			 * - empty means exactly empty;
			 * - other means starts with.
			 * If this became insufficient, we could envision using specific
			 * matchers for specific needs.
			 */
			System.out.println(Util.displayString(platformIndependantString, 2));
			this.failureReason =
				"Unexpected error running resulting class file for "
					+ sourceFileName
					+ ":\n"
					+ "--[START]--\n"
					+ errorString
					+ "---[END]---\n";
			didMatchExpectation = false;
		}
	} else if (trimmedErrorString.length() != 0){
		platformIndependantString = Util.convertToIndependantLineDelimiter(trimmedErrorString);
		System.out.println(Util.displayString(platformIndependantString, 2));
		this.failureReason =
			"Unexpected error running resulting class file for "
				+ sourceFileName
				+ ":\n"
				+ "--[START]--\n"
				+ errorString
				+ "---[END]---\n";
		didMatchExpectation = false;
	}
	return didMatchExpectation;
}

private boolean checkBuffersThrowingError(String errorString, String sourceFileName, String expectedSuccessOutputString) {

	if (errorString.length() > 0 && errorString.indexOf(expectedSuccessOutputString) != -1) {
		return true;
	}

	this.failureReason =
		"Expected error not thrown for "
			+ sourceFileName
			+ ":\n"
			+ expectedSuccessOutputString;
	return false;
}

private void compileVerifyTests(String verifierDir) {
	String fullyQualifiedName = VerifyTests.class.getName();

	int lastDot = fullyQualifiedName.lastIndexOf('.');
	String packageName = fullyQualifiedName.substring(0, lastDot);
	String simpleName = fullyQualifiedName.substring(lastDot + 1);

	String dirName = verifierDir.replace('\\', '/') + "/" + packageName.replace('.', '/');
	File dir = new File(dirName.replace('/', File.separatorChar));
	if (!dir.exists() && !dir.mkdirs()) {
		System.out.println("Could not create " + dir);
		return;
	}
	String fileName = dir + File.separator + simpleName + ".java";
	Util.writeToFile(getVerifyTestsCode(), fileName);
	BatchCompiler.compile("\"" + fileName + "\" -d \"" + verifierDir + "\" -warn:-resource -classpath \"" + Util.getJavaClassLibsAsString() + "\"", new PrintWriter(System.out), new PrintWriter(System.err), null/*progress*/);
}
public void execute(String className, String[] classpaths) {
	this.outputBuffer = new StringBuffer();
	this.errorBuffer = new StringBuffer();

	launchAndRun(className, classpaths, null, null);
}
protected void finalize() throws Throwable {
	shutDown();
}
public String getExecutionOutput(){
	return this.outputBuffer.toString();
}

public String getExecutionError(){
	return this.errorBuffer.toString();
}
/**
 * Returns the code of the VerifyTests class.
 *
 * IMPORTANT NOTE: DO NOTE EDIT BUT GENERATE INSTEAD (see below)
 *
 * To generate:
 * - export VerifyTests.java to d:/temp
 * - inspect org.eclipse.jdt.core.tests.util.Util.fileContentToDisplayString("d:/temp/VerifyTests.java", 2, true)
 */
private String getVerifyTestsCode() {
	return
		"/*******************************************************************************\n" + 
		" * Copyright (c) 2000, 2017 IBM Corporation and others.\n" + 
		" * All rights reserved. This program and the accompanying materials\n" + 
		" * are made available under the terms of the Eclipse Public License v1.0\n" + 
		" * which accompanies this distribution, and is available at\n" + 
		" * http://www.eclipse.org/legal/epl-v10.html\n" + 
		" *\n" + 
		" * Contributors:\n" + 
		" *     IBM Corporation - initial API and implementation\n" + 
		" *******************************************************************************/\n" + 
		"package org.eclipse.jdt.core.tests.util;\n" + 
		"\n" + 
		"import java.io.DataInputStream;\n" + 
		"import java.io.DataOutputStream;\n" + 
		"import java.io.File;\n" + 
		"import java.io.FileInputStream;\n" + 
		"import java.io.FileNotFoundException;\n" + 
		"import java.io.IOException;\n" + 
		"import java.io.InputStream;\n" + 
		"import java.lang.reflect.InvocationTargetException;\n" + 
		"import java.lang.reflect.Method;\n" + 
		"import java.net.Socket;\n" + 
		"import java.util.StringTokenizer;\n" + 
		"\n" + 
		"/******************************************************\n" + 
		" *\n" + 
		" * IMPORTANT NOTE: If modifying this class, copy the source to TestVerifier#getVerifyTestsCode()\n" + 
		" * (see this method for details)\n" + 
		" *\n" + 
		" ******************************************************/\n" + 
		"\n" + 
		"public class VerifyTests {\n" + 
		"	int portNumber;\n" + 
		"	Socket socket;\n" + 
		"\n" + 
		"/**\n" + 
		" * NOTE: Code copied from junit.util.TestCaseClassLoader.\n" + 
		" *\n" + 
		" * A custom class loader which enables the reloading\n" + 
		" * of classes for each test run. The class loader\n" + 
		" * can be configured with a list of package paths that\n" + 
		" * should be excluded from loading. The loading\n" + 
		" * of these packages is delegated to the system class\n" + 
		" * loader. They will be shared across test runs.\n" + 
		" * <p>\n" + 
		" * The list of excluded package paths is specified in\n" + 
		" * a properties file \"excluded.properties\" that is located in\n" + 
		" * the same place as the TestCaseClassLoader class.\n" + 
		" * <p>\n" + 
		" * <b>Known limitation:</b> the VerifyClassLoader cannot load classes\n" + 
		" * from jar files.\n" + 
		" */\n" + 
		"\n" + 
		"\n" + 
		"public class VerifyClassLoader extends ClassLoader {\n" + 
		"	/** scanned class path */\n" + 
		"	private String[] pathItems;\n" + 
		"\n" + 
		"	/** excluded paths */\n" + 
		"	private String[] excluded= {};\n" + 
		"\n" + 
		"	/**\n" + 
		"	 * Constructs a VerifyClassLoader. It scans the class path\n" + 
		"	 * and the excluded package paths\n" + 
		"	 */\n" + 
		"	public VerifyClassLoader() {\n" + 
		"		super();\n" + 
		"		String classPath= System.getProperty(\"java.class.path\");\n" + 
		"		String separator= System.getProperty(\"path.separator\");\n" + 
		"\n" + 
		"		// first pass: count elements\n" + 
		"		StringTokenizer st= new StringTokenizer(classPath, separator);\n" + 
		"		int i= 0;\n" + 
		"		while (st.hasMoreTokens()) {\n" + 
		"			st.nextToken();\n" + 
		"			i++;\n" + 
		"		}\n" + 
		"		// second pass: split\n" + 
		"		this.pathItems= new String[i];\n" + 
		"		st= new StringTokenizer(classPath, separator);\n" + 
		"		i= 0;\n" + 
		"		while (st.hasMoreTokens()) {\n" + 
		"			this.pathItems[i++]= st.nextToken();\n" + 
		"		}\n" + 
		"\n" + 
		"	}\n" + 
		"	public java.net.URL getResource(String name) {\n" + 
		"		return ClassLoader.getSystemResource(name);\n" + 
		"	}\n" + 
		"	public InputStream getResourceAsStream(String name) {\n" + 
		"		return ClassLoader.getSystemResourceAsStream(name);\n" + 
		"	}\n" + 
		"	protected boolean isExcluded(String name) {\n" + 
		"		// exclude the \"java\" packages.\n" + 
		"		// They always need to be excluded so that they are loaded by the system class loader\n" + 
		"		if (name.startsWith(\"java\") || name.startsWith(\"[Ljava\"))\n" + 
		"			return true;\n" + 
		"\n" + 
		"		// exclude the user defined package paths\n" + 
		"		for (int i= 0; i < this.excluded.length; i++) {\n" + 
		"			if (name.startsWith(this.excluded[i])) {\n" + 
		"				return true;\n" + 
		"			}\n" + 
		"		}\n" + 
		"		return false;\n" + 
		"	}\n" + 
		"	public synchronized Class loadClass(String name, boolean resolve)\n" + 
		"		throws ClassNotFoundException {\n" + 
		"\n" + 
		"		Class c= findLoadedClass(name);\n" + 
		"		if (c != null)\n" + 
		"			return c;\n" + 
		"		//\n" + 
		"		// Delegate the loading of excluded classes to the\n" + 
		"		// standard class loader.\n" + 
		"		//\n" + 
		"		if (isExcluded(name)) {\n" + 
		"			try {\n" + 
		"				c= findSystemClass(name);\n" + 
		"				return c;\n" + 
		"			} catch (ClassNotFoundException e) {\n" + 
		"				// keep searching\n" + 
		"			}\n" + 
		"		}\n" + 
		"		File file= locate(name);\n" + 
		"		if (file == null)\n" + 
		"			throw new ClassNotFoundException();\n" + 
		"		byte data[]= loadClassData(file);\n" + 
		"		c= defineClass(name, data, 0, data.length);\n" + 
		"		if (resolve)\n" + 
		"			resolveClass(c);\n" + 
		"		return c;\n" + 
		"	}\n" + 
		"	private byte[] loadClassData(File f) throws ClassNotFoundException {\n" + 
		"		FileInputStream stream = null;\n" + 
		"		try {\n" + 
		"			//System.out.println(\"loading: \"+f.getPath());\n" + 
		"			stream = new FileInputStream(f);\n" + 
		"\n" + 
		"			try {\n" + 
		"				byte[] b= new byte[stream.available()];\n" + 
		"				stream.read(b);\n" + 
		"				return b;\n" + 
		"			}\n" + 
		"			catch (IOException e) {\n" + 
		"				throw new ClassNotFoundException();\n" + 
		"			}\n" + 
		"		}\n" + 
		"		catch (FileNotFoundException e) {\n" + 
		"			throw new ClassNotFoundException();\n" + 
		"		} finally {\n" + 
		"			if (stream != null) {\n" + 
		"				try {\n" + 
		"					stream.close();\n" + 
		"				} catch (IOException e) {\n" + 
		"					/* ignore */\n" + 
		"				}\n" + 
		"			}\n" + 
		"		}\n" + 
		"	}\n" + 
		"	/**\n" + 
		"	 * Locate the given file.\n" + 
		"	 * @return Returns null if file couldn't be found.\n" + 
		"	 */\n" + 
		"	private File locate(String fileName) {\n" + 
		"		if (fileName != null) {\n" + 
		"			fileName= fileName.replace('.', '/')+\".class\";\n" + 
		"			File path= null;\n" + 
		"			for (int i= 0; i < this.pathItems.length; i++) {\n" + 
		"				path= new File(this.pathItems[i], fileName);\n" + 
		"				if (path.exists())\n" + 
		"					return path;\n" + 
		"			}\n" + 
		"		}\n" + 
		"		return null;\n" + 
		"	}\n" + 
		"}\n" + 
		"\n" + 
		"public void loadAndRun(String className) throws Throwable {\n" + 
		"	//System.out.println(\"Loading \" + className + \"...\");\n" + 
		"	Class testClass = new VerifyClassLoader().loadClass(className);\n" + 
		"	//System.out.println(\"Loaded \" + className);\n" + 
		"	try {\n" + 
		"		Method main = testClass.getMethod(\"main\", new Class[] {String[].class});\n" + 
		"		//System.out.println(\"Running \" + className);\n" + 
		"		main.invoke(null, new Object[] {new String[] {}});\n" + 
		"		//System.out.println(\"Finished running \" + className);\n" + 
		"	} catch (NoSuchMethodException e) {\n" + 
		"		return;\n" + 
		"	} catch (InvocationTargetException e) {\n" + 
		"		throw e.getTargetException();\n" + 
		"	}\n" + 
		"}\n" + 
		"public static void main(String[] args) throws IOException {\n" + 
		"	VerifyTests verify = new VerifyTests();\n" + 
		"	verify.portNumber = Integer.parseInt(args[0]);\n" + 
		"	verify.run();\n" + 
		"}\n" + 
		"public void run() throws IOException {\n" + 
		"	this.socket = new Socket(\"localhost\", this.portNumber);\n" + 
		"	this.socket.setTcpNoDelay(true);\n" + 
		"\n" + 
		"	DataInputStream in = new DataInputStream(this.socket.getInputStream());\n" + 
		"	final DataOutputStream out = new DataOutputStream(this.socket.getOutputStream());\n" + 
		"	while (true) {\n" + 
		"		final String className = in.readUTF();\n" + 
		"		Thread thread = new Thread() {\n" + 
		"			public void run() {\n" + 
		"				try {\n" + 
		"					loadAndRun(className);\n" + 
		"					out.writeBoolean(true);\n" + 
		"					System.err.println(VerifyTests.class.getName());\n" + 
		"					System.out.println(VerifyTests.class.getName());\n" + 
		"				} catch (Throwable e) {\n" + 
		"					e.printStackTrace();\n" + 
		"					try {\n" + 
		"						System.err.println(VerifyTests.class.getName());\n" + 
		"						System.out.println(VerifyTests.class.getName());\n" + 
		"						out.writeBoolean(false);\n" + 
		"					} catch (IOException e1) {\n" + 
		"						e1.printStackTrace();\n" + 
		"					}\n" + 
		"				}\n" + 
		"				try {\n" + 
		"					out.flush();\n" + 
		"				} catch (IOException e) {\n" + 
		"					e.printStackTrace();\n" + 
		"				}\n" + 
		"			}\n" + 
		"		};\n" + 
		"		thread.start();\n" + 
		"	}\n" + 
		"}\n" + 
		"}";
}
private void launchAndRun(String className, String[] classpaths, String[] programArguments, String[] vmArguments) {
	// we won't reuse the vm, shut the existing one if running
	if (this.vm != null) {
		try {
			this.vm.shutDown();
		} catch (TargetException e) {
		}
	}
	this.classpathCache = null;

	// launch a new one
	LocalVMLauncher launcher = LocalVMLauncher.getLauncher();
	launcher.setClassPath(classpaths);
	launcher.setVMPath(Util.getJREDirectory());
	if (vmArguments != null) {
		String[] completeVmArguments = new String[vmArguments.length + 1];
		System.arraycopy(vmArguments, 0, completeVmArguments, 1, vmArguments.length);
		completeVmArguments[0] = "-verify";
		launcher.setVMArguments(completeVmArguments);
	} else {
		launcher.setVMArguments(new String[] {"-verify"});
	}
	launcher.setProgramClass(className);
	launcher.setProgramArguments(programArguments);
	Thread outputThread;
	Thread errorThread;
	try {
		this.vm = launcher.launch();
		final InputStream input = this.vm.getInputStream();
		outputThread = new Thread(new Runnable() {
			public void run() {
				try {
					int c = input.read();
					while (c != -1) {
						TestVerifier.this.outputBuffer.append((char) c);
						c = input.read();
					}
				} catch(IOException e) {
				}
			}
		});
		final InputStream errorStream = this.vm.getErrorStream();
		errorThread = new Thread(new Runnable() {
			public void run() {
				try {
					int c = errorStream.read();
					while (c != -1) {
						TestVerifier.this.errorBuffer.append((char) c);
						c = errorStream.read();
					}
				} catch(IOException e) {
				}
			}
		});
		outputThread.start();
		errorThread.start();
	} catch(TargetException e) {
		throw new Error(e.getMessage());
	}

	// wait for vm to shut down by itself
	try {
		outputThread.join(10000); // we shut VMs down forcefully downstream,
		errorThread.join(10000);  // hence let's have some slack here
	} catch (InterruptedException e) {
	}
}
private void launchVerifyTestsIfNeeded(String[] classpaths, String[] vmArguments) {
	// determine if we can reuse the vm
	if (this.vm != null && this.vm.isRunning() && this.classpathCache != null) {
		if (classpaths.length == this.classpathCache.length) {
			boolean sameClasspaths = true;
			for (int i = 0; i < classpaths.length; i++) {
				if (!this.classpathCache[i].equals(classpaths[i])) {
					sameClasspaths = false;
					break;
				}
			}
			if (sameClasspaths) {
				return;
			}
		}
	}

	// we could not reuse the vm, shut the existing one if running
	if (this.vm != null) {
		try {
			this.vm.shutDown();
		} catch (TargetException e) {
		}
	}

	this.classpathCache = classpaths;

	// launch a new one
	LocalVMLauncher launcher = LocalVMLauncher.getLauncher();
	int length = classpaths.length;
	String[] cp = new String[length + 1];
	System.arraycopy(classpaths, 0, cp, 0, length);
	String verifierDir = Util.getOutputDirectory() + File.separator + "verifier";
	compileVerifyTests(verifierDir);
	cp[length] = verifierDir;
	launcher.setClassPath(cp);
	launcher.setVMPath(Util.getJREDirectory());
	if (vmArguments != null) {
		String[] completeVmArguments = new String[vmArguments.length + 1];
		System.arraycopy(vmArguments, 0, completeVmArguments, 1, vmArguments.length);
		completeVmArguments[0] = "-verify";
		launcher.setVMArguments(completeVmArguments);
	} else {
		launcher.setVMArguments(new String[] {"-verify"});
	}
	launcher.setProgramClass(VerifyTests.class.getName());
	try (ServerSocket server = new ServerSocket(0)) {
		int portNumber = server.getLocalPort();

		launcher.setProgramArguments(new String[] {Integer.toString(portNumber)});
		try {
			this.vm = launcher.launch();
			final InputStream input = this.vm.getInputStream();
			Thread outputThread = new Thread(new Runnable() {
				public void run() {
					try {
						int c = input.read();
						while (c != -1) {
							TestVerifier.this.outputBuffer.append((char) c);
							c = input.read();
						}
					} catch(IOException ioEx) {
					}
				}
			});
			final InputStream errorStream = this.vm.getErrorStream();
			Thread errorThread = new Thread(new Runnable() {
				public void run() {
					try {
						int c = errorStream.read();
						while (c != -1) {
							TestVerifier.this.errorBuffer.append((char) c);
							c = errorStream.read();
						}
					} catch(IOException ioEx) {
					}
				}
			});
			outputThread.start();
			errorThread.start();
		} catch(TargetException e) {
			throw new Error(e.getMessage());
		}
	
		// connect to the vm
		this.socket = null;
		boolean isVMRunning = false;
		do {
			try {
				this.socket = server.accept();
				this.socket.setTcpNoDelay(true);
				break;
			} catch (UnknownHostException e) {
			} catch (IOException e) {
			}
			if (this.socket == null) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
				}
				isVMRunning = this.vm.isRunning();
			}
		} while (this.socket == null && isVMRunning);
	} catch (IOException e) {
		throw new Error(e.getMessage());
	}
}
/**
 * Loads and runs the given class.
 * Return whether no exception was thrown while running the class.
 */
private boolean loadAndRun(String className) {
	if (this.socket != null) {
		try {
			DataOutputStream out = new DataOutputStream(this.socket.getOutputStream());
			out.writeUTF(className);
			DataInputStream in = new DataInputStream(this.socket.getInputStream());
			try {
				boolean result = in.readBoolean();
				waitForFullBuffers();
				return result;
			} catch (SocketException e) {
				// connection was reset because target program has exited
				return true;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	return true;
}
public void shutDown() {
	// Close the socket first so that the OS resource has a chance to be freed.
	if (this.socket != null) {
		try {
			this.socket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	// Wait for the vm to shut down by itself for 2 seconds. If not succesfull, force the shut down.
	if (this.vm != null) {
		try {
			int retry = 0;
			while (this.vm.isRunning() && (++retry < 20)) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
				}
			}
			if (this.vm.isRunning()) {
				this.vm.shutDown();
			}
		} catch (TargetException e) {
			e.printStackTrace();
		}
	}
}
/**
 * Verify that the class files created for the given test file can be loaded by
 * a virtual machine.
 */
public boolean verifyClassFiles(String sourceFilePath, String className, String expectedSuccessOutputString, String[] classpaths) {
	return verifyClassFiles(sourceFilePath, className, expectedSuccessOutputString, "", classpaths, null, null);
}
/**
 * Verify that the class files created for the given test file can be loaded by
 * a virtual machine.
 */
public boolean verifyClassFiles(String sourceFilePath, String className, String expectedSuccessOutputString, String[] classpaths, String[] programArguments, String[] vmArguments) {
	return verifyClassFiles(sourceFilePath, className, expectedSuccessOutputString, "", classpaths, programArguments, vmArguments);
}
public boolean verifyClassFiles(String sourceFilePath, String className, String expectedOutputString,
		String expectedErrorStringStart, String[] classpaths, String[] programArguments, String[] vmArguments) {
	this.outputBuffer = new StringBuffer();
	this.errorBuffer = new StringBuffer();
	if (this.reuseVM && programArguments == null) {
		launchVerifyTestsIfNeeded(classpaths, vmArguments);
		loadAndRun(className);
	} else {
		launchAndRun(className, classpaths, programArguments, vmArguments);
	}

	this.failureReason = null;
	return checkBuffers(this.outputBuffer.toString(), this.errorBuffer.toString(), sourceFilePath, expectedOutputString, expectedErrorStringStart);
}

/**
 * Verify that the class files created for the given test file can be loaded and run with an expected error contained
 * in the expectedSuccessOutputString string.
 */
public boolean verifyClassFilesThrowingError(String sourceFilePath, String className, String expectedSuccessOutputString, String[] classpaths, String[] programArguments, String[] vmArguments) {
	this.outputBuffer = new StringBuffer();
	this.errorBuffer = new StringBuffer();
	if (this.reuseVM && programArguments == null) {
		launchVerifyTestsIfNeeded(classpaths, vmArguments);
		loadAndRun(className);
	} else {
		launchAndRun(className, classpaths, programArguments, vmArguments);
	}

	this.failureReason = null;
	return checkBuffersThrowingError(this.errorBuffer.toString(), sourceFilePath, expectedSuccessOutputString);
}

/**
 * Wait until there is nothing more to read from the stdout or sterr.
 */
private void waitForFullBuffers() {
	String endString = VerifyTests.class.getName();
	int count = 50;
	int errorEndStringStart = this.errorBuffer.toString().indexOf(endString);
	int outputEndStringStart = this.outputBuffer.toString().indexOf(endString);
	while (errorEndStringStart == -1 || outputEndStringStart == -1) {
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
		}
		if (--count == 0) return;
		errorEndStringStart = this.errorBuffer.toString().indexOf(endString);
		outputEndStringStart = this.outputBuffer.toString().indexOf(endString);
	}
	this.errorBuffer.setLength(errorEndStringStart);
	this.outputBuffer.setLength(outputEndStringStart);
}
}
