/***************************************************************************
 * Copyright 2012 Kieker Project (http://kieker-monitoring.net)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ***************************************************************************/

package kieker.webgui.common;

import java.io.File;
import java.io.IOException;
import java.util.Collection;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.RegexFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.junit.Assert;
import org.junit.Test;

import kieker.webgui.common.exception.AbstractKiekerWebGUIException;

/**
 * This JUnit test makes sure that all exceptions within the application extend the {@link AbstractKiekerWebGUIException}. The exceptions in question will be found
 * using two criteria: They have to be in a package containing the subpackage {@code exception} and their classname has to contain {@code Exception}.
 * 
 * @author Nils Christian Ehmke
 */
public class ExceptionsExtendingAbstractClassTest {

	private static final String UNNECESSARY_PREFIX = "src.main.java.";
	private static final String UNNECESSARY_SUFFIX = ".java";
	private static final String PATTERN_EXCEPTION_PACKAGE = File.separator + "exception" + File.separator;
	private static final String DIR_NAME_SOURCES = "src";
	private static final String PATTERN_EXCEPTION_SOURCE_FILES = ".*Exception\\.java";

	/**
	 * Default constructor. <b>Do not use this constructor. This is just a test class and not to be used outside a JUnit test!</b>
	 */
	public ExceptionsExtendingAbstractClassTest() {
		// No code necessary
	}

	/**
	 * This is the main test running through the available JUnit tests and checking them.
	 * 
	 * @throws IOException
	 *             If something went wrong during the analysis.
	 * 
	 * @throws ClassNotFoundException
	 *             If one of the JUnit test classes could not be loaded.
	 */
	@Test
	public void test() throws IOException, ClassNotFoundException {
		final Collection<File> sourceFiles = ExceptionsExtendingAbstractClassTest.listJavaSourceFiles();
		for (final File sourceFile : sourceFiles) {
			if (ExceptionsExtendingAbstractClassTest.isSourceFileInExceptionPackage(sourceFile)) {
				final String className = ExceptionsExtendingAbstractClassTest.sourceFileToClassName(sourceFile);
				final Class<?> clazz = this.getClass().getClassLoader().loadClass(className); // NOPMD (Test)
				if (!ExceptionsExtendingAbstractClassTest.doesClassExtendAbstractKiekerWebGUIException(clazz)) {
					Assert.fail("Class '" + className + "' doesn't extend AbstractKiekerWebGUIException");
				}
			}
		}
	}

	private static Collection<File> listJavaSourceFiles() {
		final IOFileFilter filter = new RegexFileFilter(PATTERN_EXCEPTION_SOURCE_FILES);
		return FileUtils.listFiles(new File(DIR_NAME_SOURCES), filter, TrueFileFilter.INSTANCE);
	}

	private static boolean isSourceFileInExceptionPackage(final File file) {
		return file.getAbsolutePath().contains(PATTERN_EXCEPTION_PACKAGE);
	}

	private static boolean doesClassExtendAbstractKiekerWebGUIException(final Class<?> clazz) {
		return AbstractKiekerWebGUIException.class.isAssignableFrom(clazz);
	}

	private static String sourceFileToClassName(final File file) {
		final String pathName = file.getPath();

		String className = pathName.replace(File.separator, ".");
		className = className.substring(UNNECESSARY_PREFIX.length());
		className = className.substring(0, className.length() - UNNECESSARY_SUFFIX.length());

		return className;
	}
}
