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

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

import junit.framework.Test;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.tests.util.Util;
import org.eclipse.jdt.internal.compiler.parser.JavadocTagConstants;

@SuppressWarnings({"rawtypes", "unchecked"})
public abstract class AbstractJavadocCompletionModelTest extends AbstractJavaModelCompletionTests implements JavadocTagConstants {

	// Basic relevance values
	/** R_DEFAULT+R_INTERESTING+R_NON_RESTRICTED<br>= 8 */
	protected final static int JAVADOC_RELEVANCE = R_DEFAULT+ R_INTERESTING+ R_NON_RESTRICTED;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING<br>+R_UNQUALIFIED+R_NON_RESTRICTED<br>= 9 */
	protected final static int R_DRINR= R_DEFAULT+R_RESOLVED+R_INTERESTING+R_NON_RESTRICTED;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING<br>+R_UNQUALIFIED+R_NON_RESTRICTED<br>= 12 */
	protected final static int R_DRIUNR= R_DEFAULT+R_RESOLVED+R_INTERESTING+R_UNQUALIFIED+R_NON_RESTRICTED;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE<br>+R_NON_RESTRICTED<br>= 19 */
	protected static final int R_DRICNR = R_DEFAULT+R_RESOLVED+R_INTERESTING+R_CASE+R_NON_RESTRICTED;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE<br>+R_QUALIFIED+R_NON_RESTRICTED<br>= 21 */
	protected static final int R_DRICQNR = R_DEFAULT+R_RESOLVED+R_INTERESTING+R_CASE+R_QUALIFIED+R_NON_RESTRICTED;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE<br>+R_UNQUALIFIED+R_NON_RESTRICTED<br>= 22 */
	protected static final int R_DRICUNR = R_DEFAULT+R_RESOLVED+R_INTERESTING+R_CASE+R_UNQUALIFIED+R_NON_RESTRICTED;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE+R_EXACT_NAME<br>+R_NON_RESTRICTED<br>= 23 */
	protected static final int R_DRICENNR = R_DEFAULT+R_RESOLVED+R_INTERESTING+R_CASE+R_EXACT_NAME+R_NON_RESTRICTED;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE+R_EXACT_NAME<br>+R_QUALIFIED+R_NON_RESTRICTED<br>= 25 */
	protected static final int R_DRICENQNR = R_DEFAULT+R_RESOLVED+R_INTERESTING+R_CASE+R_EXACT_NAME+R_QUALIFIED+R_NON_RESTRICTED;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE+R_EXACT_NAME<br>+R_UNQUALIFIED+R_NON_RESTRICTED<br>= 26 */
	protected static final int R_DRICENUNR = R_DEFAULT+R_RESOLVED+R_INTERESTING+R_CASE+R_EXACT_NAME+R_UNQUALIFIED+R_NON_RESTRICTED;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE<br>+R_NON_RESTRICTED+R_NON_STATIC<br>= 30 */
	protected static final int R_DRICNRNS = R_DEFAULT+R_RESOLVED+R_INTERESTING+R_CASE+R_NON_RESTRICTED+R_NON_STATIC;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE+R_EXACT_NAME<br>+R_NON_RESTRICTED+R_NON_STATIC<br>= 34 */
	protected static final int R_DRICENNRNS = R_DEFAULT+R_RESOLVED+R_INTERESTING+R_CASE+R_EXACT_NAME+R_NON_RESTRICTED+R_NON_STATIC;

	// Exception relevance values
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE<br>+R_NON_RESTRICTED+R_EXCEPTION<br>= 39 */
	protected static final int R_DRICNRE = R_DRICNR+R_EXCEPTION;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE<br>+R_UNQUALIFIED+R_NON_RESTRICTED+R_EXCEPTION<br>= 42 */
	protected static final int R_DRICUNRE = R_DRICUNR+R_EXCEPTION;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE+R_EXACT_NAME<br>+R_NON_RESTRICTED+R_EXCEPTION<br>= 43 */
	protected static final int R_DRICENNRE = R_DRICENNR+R_EXCEPTION;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE+R_EXACT_NAME<br>+R_UNQUALIFIED+R_NON_RESTRICTED+R_EXCEPTION<br>= 46 */
	protected static final int R_DRICENUNRE = R_DRICENUNR+R_EXCEPTION;

	// Exact Expected relevance values
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE<br>+R_NON_RESTRICTED+R_EXACT_EXPECTED_TYPE<br>= 49 */
	protected static final int R_DRICNREET = R_DRICNR+R_EXACT_EXPECTED_TYPE;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE+R_UNQUALIFIED<br>+R_NON_RESTRICTED+R_EXACT_EXPECTED_TYPE<br>= 52 */
	protected static final int R_DRICUNREET = R_DRICUNR+R_EXACT_EXPECTED_TYPE;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE+R_UNQUALIFIED<br>+R_NON_RESTRICTED+R_EXACT_EXPECTED_TYPE+R_EXCEPTION<br>= 72 */
	protected static final int R_DRICUNREETE = R_DRICUNR+R_EXACT_EXPECTED_TYPE+R_EXCEPTION;

	// Exact Expected Exception relevance values
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE+R_NON_RESTRICTED<br>+R_EXCEPTION+R_EXACT_EXPECTED_TYPE<br>= 69 */
	protected static final int R_DRICNREEET = R_DRICNRE+R_EXACT_EXPECTED_TYPE;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE+R_UNQUALIFIED<br>+R_NON_RESTRICTED+R_EXCEPTION<br>
	 * +R_EXACT_EXPECTED_TYPE<br>= 72 */
	protected static final int R_DRICUNREEET = R_DRICUNRE+R_EXACT_EXPECTED_TYPE;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE+R_EXACT_NAME<br>+R_NON_RESTRICTED+R_EXCEPTION<br>
	 * +R_EXACT_EXPECTED_TYPE<br>= 73 */
	protected static final int R_DRICENNREEET = R_DRICENNRE+R_EXACT_EXPECTED_TYPE;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE+R_EXACT_NAME<br>+R_UNQUALIFIED+R_NON_RESTRICTED+R_EXCEPTION<br>
	 * +R_EXACT_EXPECTED_TYPE<br>= 76 */
	protected static final int R_DRICENUNREEET = R_DRICENUNRE+R_EXACT_EXPECTED_TYPE;

	// Inline tag relevance values
	/** R_DEFAULT+R_INTERESTING+R_NON_RESTRICTED<br>+R_INLINE_TAG<br>= 39 */
	protected static final int JAVADOC_RELEVANCE_IT = JAVADOC_RELEVANCE+R_INLINE_TAG;
	/** R_DEFAULT+R_RESOLVED<br+R_INTERESTING+R_NON_RESTRICTED<br>+R_INLINE_TAG<br>= 40 */
	protected static final int R_DRINRIT = R_DRINR+R_INLINE_TAG;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE<br>+R_NON_RESTRICTED+R_INLINE_TAG<br>= 50 */
	protected static final int R_DRICNRIT = R_DRICNR+R_INLINE_TAG;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE+R_UNQUALIFIED<br>+R_NON_RESTRICTED+R_INLINE_TAG<br>= 53 */
	protected static final int R_DRICUNRIT = R_DRICUNR+R_INLINE_TAG;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE+R_EXACT_NAME<br>+R_NON_RESTRICTED+R_INLINE_TAG<br>= 54 */
	protected static final int R_DRICENNRIT = R_DRICENNR+R_INLINE_TAG;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE+R_EXACT_NAME<br>+R_UNQUALIFIED+R_NON_RESTRICTED+R_INLINE_TAG<br>= 57 */
	protected static final int R_DRICENUNRIT = R_DRICENUNR+R_INLINE_TAG;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE<br>+R_NON_RESTRICTED+R_NON_STATIC+R_INLINE_TAG<br>= 61 */
	protected static final int R_DRICNRNSIT = R_DRICNRNS+R_INLINE_TAG;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE+R_NON_RESTRICTED<br>+R_EXACT_EXPECTED_TYPE+R_INLINE_TAG<br>= 80 */
	protected static final int R_DRICNREETIT = R_DRICNREET+R_INLINE_TAG;
	/** R_DEFAULT+R_RESOLVED<br>+R_INTERESTING+R_CASE+R_UNQUALIFIED<br>+R_NON_RESTRICTED+R_EXACT_EXPECTED_TYPE<br>
	 * +R_INLINE_TAG<br>= 83 */
	protected static final int R_DRICUNREETIT = R_DRICUNREET+R_INLINE_TAG;

	// Store all relevance values in array
	private static final int[] RELEVANCES = {
		JAVADOC_RELEVANCE,
		R_DRIUNR,
		R_DRICNR,
		R_DRICQNR,
		R_DRICUNR,
		R_DRICENNR,
		R_DRICENQNR,
		R_DRICENUNR,
		R_DRICNRNS,
		R_DRICENNRNS,
		R_DRICNRE,
		R_DRICUNRE,
		R_DRICENNRE,
		R_DRICENUNRE,
		R_DRICNREET,
		R_DRICUNREET,
		R_DRICNREEET,
		R_DRICUNREEET,
		R_DRICENNREEET,
		R_DRICENUNREEET,
		JAVADOC_RELEVANCE_IT,
		R_DRICNRIT,
		R_DRICUNRIT,
		R_DRICENNRIT,
		R_DRICENUNRIT,
		R_DRICNRNSIT,
		R_DRICNREETIT,
		R_DRICUNREETIT,
	};
	private static final String[] RELEVANCES_NAMES = {
		"JAVADOC_RELEVANCE",
		"R_DRIUNR",
		"R_DRICNR",
		"R_DRICQNR",
		"R_DRICUNR",
		"R_DRICENNR",
		"R_DRICENQNR",
		"R_DRICENUNR",
		"R_DRICNRNS",
		"R_DRICENNRNS",
		"R_DRICNRE",
		"R_DRICUNRE",
		"R_DRICENNRE",
		"R_DRICENUNRE",
		"R_DRICNREET",
		"R_DRICUNREET",
		"R_DRICNREEET",
		"R_DRICUNREEET",
		"R_DRICENNREEET",
		"R_DRICENUNREEET",
		"JAVADOC_RELEVANCE_IT",
		"R_DRICNRIT",
		"R_DRICUNRIT",
		"R_DRICENNRIT",
		"R_DRICENUNRIT",
		"R_DRICNRNSIT",
		"R_DRICNREETIT",
		"R_DRICUNREETIT",
	};

	// Write file contents
	protected static final String WRITE_DIR = System.getProperty("writeDir");
	protected static final File WRITE_DIR_FILE;
	protected static final Set PACKAGE_FILES = new HashSet();
	static {
		File writeDir = null;
		if (WRITE_DIR != null) {
			// Create write directory if necessay
			writeDir = new File(WRITE_DIR);
			if (writeDir.exists()) {
				// perhaps delete all files...
			} else if (!writeDir.mkdirs()) {
				System.err.println(WRITE_DIR+" does NOT exist and cannot be created!!!");
				writeDir = null;
			}

		}
		WRITE_DIR_FILE = writeDir;
	}

	CompletionTestsRequestor2 requestor;
	protected int cursorLocation;
	protected int completionStart;
	protected String replacedText;
	protected String positions;

	public AbstractJavadocCompletionModelTest(String name) {
		super(name);
		this.tabs = 2;
		this.displayName = true;
		this.endChar = "";
	}

	public static Test suite() {
		return buildModelTestSuite(AbstractJavadocCompletionModelTest.class);
	}
	protected void assertResults(String expected) throws JavaModelException {
		int length = this.workingCopies.length;
		String[] sources = new String[length*2];
		for (int i=0; i<length; i++) {
			sources[i*2] = this.workingCopies[i].getPath().toString();
			sources[i*2+1] = this.workingCopies[i].getSource();
		}
		assertResults(sources, expected, this.requestor.getResultsWithoutSorting());
	}
	protected void assertSortedResults(String expected) throws JavaModelException {
		int length = this.workingCopies.length;
		String[] sources = new String[length*2];
		for (int i=0; i<length; i++) {
			sources[i*2] = this.workingCopies[i].getPath().toString();
			sources[i*2+1] = this.workingCopies[i].getSource();
		}
		assertResults(sources, expected, this.requestor.getReversedResults());
	}
	private void assertResults(String[] sources, String expected, String actual) {
		int count = this.requestor.proposalsPtr+1;
		if (!expected.equals(actual)) {
			System.out.println("********************************************************************************");
			if (this.displayName) {
				System.out.print(getName());
				System.out.print(" got ");
				if (count==0)
					System.out.println("no result!");
				else {
					System.out.print(count);
					System.out.print(" result");
					if (count==1)
						System.out.println(":");
					else
						System.out.println("s:");
				}
			}
			if (!this.displayName || count>0) {
				System.out.println(displayString(actual, this.tabs));
				System.out.println(this.endChar);
			}
			System.out.println("--------------------------------------------------------------------------------");
			for (int i=0, length = sources.length; i<length; i+=2) {
				System.out.println(sources[i]);
				System.out.println(sources[i+1]);
			}
		}
		assertEquals(
			"Completion proposals are not correct!",
			expected,
			actual
		);
	}
	protected void assertNoProblem(String path) {
		String problem = this.requestor.getProblem();
		if (problem.length() > 0) {
			System.out.println("********************************************************************************");
			if (this.displayName) {
				System.out.print(getName());
				System.out.println(" contains an error although it should NOT:");
			}
			System.out.println(displayString(problem, this.tabs));
			System.out.println("--------------------------------------------------------------------------------");
			System.out.println(this.workingCopies[0].getPath().toString()+'\n');
			try {
				System.out.println(this.workingCopies[0].getSource());
			} catch (JavaModelException e) {
				// forget it
			}
			assertEquals(
				path+" should have NO problem!",
				"",
				problem
			);
		}
	}

	protected void completeInJavadoc(String path, String source, boolean showPositions, String completeBehind) throws JavaModelException {
		completeInJavadoc(path, source, showPositions, completeBehind, 1 /* first index */);
	}

	protected void completeInJavadoc(String path, String source, boolean showPositions, String completeBehind, boolean last) throws JavaModelException {
		completeInJavadoc(path, source, showPositions, completeBehind, last ? -1 : 1);
	}

	protected void completeInJavadoc(String path, String source, boolean showPositions, String completeBehind, int occurencePosition) throws JavaModelException {
		completeInJavadoc(new String[] { path, source }, showPositions, completeBehind, occurencePosition, null);
	}

	protected void completeInJavadoc(String path, String source, boolean showPositions, String completeBehind, int occurencePosition, int[] ignoreList) throws JavaModelException {
		completeInJavadoc(new String[] { path, source }, showPositions, completeBehind, occurencePosition, ignoreList);
	}
	protected void completeInJavadoc(String[] sources, boolean showPositions, String completeBehind) throws JavaModelException {
		completeInJavadoc(sources, showPositions, completeBehind, 1, null);
	}

	protected void completeInJavadoc(String[] sources, boolean showPositions, String completeBehind, int occurencePosition) throws JavaModelException {
		completeInJavadoc(sources, showPositions, completeBehind, occurencePosition, null);
	}

	protected void completeInJavadoc(String[] sources, boolean showPositions, String completeBehind, int occurencePosition, int[] ignoreList) throws JavaModelException {
		assertNotNull("We should have sources!!!", sources);
		assertTrue("Invalid number of sources!!!",  sources.length%2==0);

		// Build working copy(ies)
		int length = sources.length / 2;
		this.workingCopies = new ICompilationUnit[length];
		for (int i=0; i<length; i++) {
			this.workingCopies[i] = getWorkingCopy(sources[i*2], sources[i*2+1]);
			if (WRITE_DIR != null) 	writeFiles(sources);
		}

		// Wait for indexes
		waitUntilIndexesReady();

		// Complete
		this.requestor = new CompletionTestsRequestor2(true, false, showPositions);
		if (ignoreList != null) {
			for (int i = 0; i < ignoreList.length; i++) {
				this.requestor.setIgnored(ignoreList[i], true);
			}
		}
		String source = this.workingCopies[0].getSource();
		this.replacedText = completeBehind;
		this.completionStart = -1;
		int cursorPos = this.replacedText.length();
		if (occurencePosition < -10) { // case where we want to specify directly the cursor location relatively to completion start
			this.completionStart = source.indexOf(this.replacedText);
			cursorPos = -occurencePosition - 10;
		} else if (occurencePosition < 0) {
			this.completionStart = source.lastIndexOf(this.replacedText);
			int max = -occurencePosition;
			for (int i=1; i<max; i++) {
				this.completionStart = source.lastIndexOf(this.replacedText, this.completionStart);
			}
		} else {
			this.completionStart = source.indexOf(this.replacedText);
			int shift = this.replacedText.length();
			for (int i=1; i<occurencePosition && this.completionStart>0; i++) {
				this.completionStart = source.indexOf(this.replacedText, this.completionStart+shift);
			}
		}
		assertTrue("We should have found "+occurencePosition+" occurence(s) of '"+this.replacedText+"' in:\n"+source, this.completionStart>0);
		this.cursorLocation =  this.completionStart + cursorPos;
		this.workingCopies[0].codeComplete(this.cursorLocation, this.requestor, this.wcOwner);
		assertNoProblem(sources[0]);

		// Store replacement info
		if (occurencePosition == 0) { // special case for completion on empty token...
			this.completionStart = this.cursorLocation;
		}
		int endPosition = this.cursorLocation;
		char ch = source.charAt(endPosition);
		if (Character.isJavaIdentifierPart(ch) || ch == '>' || ch == '}' || ch == '(' || ch == ')') {
			do {
				ch = source.charAt(++endPosition);
			} while (Character.isJavaIdentifierPart(ch) || ch == '>' || ch == '}' || ch == '(' || ch == ')');
		}
		this.positions = "["+this.completionStart+", "+endPosition+"], ";
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdt.core.tests.model.AbstractJavaModelTests#displayString(java.lang.String, int)
	 */
	protected String displayString(String toPrint, int indent) {
		String toDisplay = super.displayString(toPrint, indent);
		int openBracket = toDisplay.indexOf(", [");
		if (openBracket > 0) {
			StringBuffer buffer = new StringBuffer();
			int closeBracket = 0;
			while (openBracket > 0) {
				buffer.append(toDisplay.substring(closeBracket, openBracket+2));
				closeBracket = toDisplay.indexOf("], ", openBracket+3);
				if (closeBracket < 0) break; // invalid
				closeBracket += 3;
				buffer.append("\"+this.positions+");
				int i=0;
				while (toDisplay.charAt(closeBracket+i) != '}') i++;
				try {
					int relevance = Integer.parseInt(toDisplay.substring(closeBracket, closeBracket+i));
					int length = RELEVANCES.length;
					boolean found = false;
					for (int r=0; !found && r<length; r++) {
						if (RELEVANCES[r] == relevance) {
							buffer.append(RELEVANCES_NAMES[r]);
							buffer.append("+\"");
							found = true;
						}
					}
					if (!found) {
						buffer.append('"');
						buffer.append(relevance);
					}
					closeBracket += i;
				}
				catch (NumberFormatException nfe) {
					System.err.println(nfe.getMessage()+" should not occur!");
				}
				openBracket = toDisplay.indexOf(", [", closeBracket);
			}
			if (closeBracket > 0) {
				buffer.append(toDisplay.substring(closeBracket, toDisplay.length()));
				toDisplay = buffer.toString();
			}
		}
		return toDisplay.replaceAll(", 8}", ", \"+JAVADOC_RELEVANCE+\"}");
	}

	protected void setUpProjectOptions(String compliance) throws JavaModelException {
		try {
			setUpProjectCompliance(COMPLETION_PROJECT, compliance);
		} catch (IOException e) {
			assertTrue("Unexpected IOException: "+e.getMessage(), false);
		}
	}

	public void setUpSuite() throws Exception {
		super.setUpSuite();
		if (COMPLETION_PROJECT == null)  {
			COMPLETION_PROJECT = setUpJavaProject("Completion");
			createFolder(new Path("/Completion/src/javadoc/tags"));
		} else {
			setUpProjectCompliance(COMPLETION_PROJECT, "1.4");
			this.currentProject = COMPLETION_PROJECT;
		}
	}

	/* (non-Javadoc)
	 * @see org.eclipse.jdt.core.tests.model.AbstractJavaModelTests#tearDown()
	 */
	protected void tearDown() throws Exception {
		this.requestor = null;
		super.tearDown();
	}

	public void tearDownSuite() throws Exception {
		deleteFolder(new Path("/Completion/src/javadoc/tags"));
		super.tearDownSuite();
		if (COMPLETION_SUITES == null) {
			COMPLETION_PROJECT = null;
		}
	}

	/*
	 * Write files for self-hosting debug.
	 */
	protected void writeFiles(String[] sources) {

		// Get write directory path
		if (WRITE_DIR_FILE == null) return;

		// Get test name
		String testName = getName();
		int idx = testName.indexOf(" - ");
		if (idx > 0) {
			testName = testName.substring(idx+3);
		}
//		testName = "Test"+testName.substring(4);

		// Write sources to dir
		int length = sources.length / 2;
		String[][] names = new String[length][3];
		for (int i=0; i<length; i++) {

			// Get pathes
			IPath filePath = new Path(sources[2*i]).removeFirstSegments(2); // remove project and source folder
			IPath dirPath = filePath.removeLastSegments(1);
			String fileDir = dirPath.toString();
			String typeName = filePath.removeFileExtension().lastSegment();

			// Create package dir or delete files if already exist
			File packageDir = new File(WRITE_DIR_FILE, fileDir);
			if (!PACKAGE_FILES.contains(packageDir)) {
				if (packageDir.exists()) {
					PACKAGE_FILES.add(packageDir);
					Util.delete(packageDir);
				} else if (packageDir.mkdirs()) {
					PACKAGE_FILES.add(packageDir);
				} else {
					System.err.println(packageDir+" does not exist and CANNOT be created!!!");
					continue;
				}
			}

			// Store names info
			names[i][0] = typeName;
			String fileName = (typeName.length() <= 3) ? typeName : typeName.substring(0, typeName.length()-3);
			fileName = fileName + testName.substring(4);
			names[i][1] = fileName;
			names[i][2] = packageDir.getAbsolutePath()+"\\"+fileName+".java";
		}

		// Write modified contents
		for (int i=0; i<length; i++) {
			String contents = sources[2*i+1];
			for (int j=0; j<length; j++) {
				contents = contents.replaceAll(names[j][0], names[j][1]);
			}
			String fullPathName = names[i][2];
			System.out.println("Write file "+fullPathName);
			Util.writeToFile(contents, fullPathName);
		}
	}
}
