/**
* 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.
*
* Copyright 2012-2015 the original author or authors.
*/
package org.assertj.maven.generator;
import static com.google.common.collect.Sets.newLinkedHashSet;
import static org.apache.commons.collections.CollectionUtils.subtract;
import static org.apache.commons.lang3.ArrayUtils.addAll;
import static org.assertj.assertions.generator.util.ClassUtil.collectClasses;
import static org.assertj.core.util.Arrays.isNullOrEmpty;
import static org.assertj.core.util.Sets.newHashSet;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.regex.Pattern;
import org.apache.maven.plugin.logging.Log;
import org.assertj.assertions.generator.AssertionsEntryPointType;
import org.assertj.assertions.generator.BaseAssertionGenerator;
import org.assertj.assertions.generator.Template;
import org.assertj.assertions.generator.description.ClassDescription;
import org.assertj.assertions.generator.description.converter.ClassToClassDescriptionConverter;
import org.assertj.core.util.VisibleForTesting;
import org.assertj.maven.Templates;
/**
* Is able to generate AssertJ assertions classes from packages.
*/
public class AssertionsGenerator {
private static final Pattern INCLUDE_EVERYTHING = Pattern.compile(".*");
private ClassToClassDescriptionConverter converter;
private ClassLoader classLoader;
private BaseAssertionGenerator generator;
private Pattern[] includePatterns;
private Pattern[] excludePatterns;
private Log log;
private Set<AssertionsEntryPointType> assertionsEntryPointToGenerate;
public AssertionsGenerator(ClassLoader classLoader) throws FileNotFoundException, IOException {
this.generator = new BaseAssertionGenerator();
this.converter = new ClassToClassDescriptionConverter();
this.classLoader = classLoader;
this.includePatterns = new Pattern[] { INCLUDE_EVERYTHING };
this.excludePatterns = new Pattern[0];
this.assertionsEntryPointToGenerate = newHashSet();
}
public void setIncludePatterns(String[] includeRegexs) {
if (isNullOrEmpty(includeRegexs)) {
includePatterns = new Pattern[] { INCLUDE_EVERYTHING };
return;
}
includePatterns = new Pattern[includeRegexs.length];
for (int i = 0; i < includeRegexs.length; i++) {
includePatterns[i] = Pattern.compile(includeRegexs[i]);
}
}
public void setExcludePatterns(String[] excludeRegexs) {
if (isNullOrEmpty(excludeRegexs)) {
return;
}
excludePatterns = new Pattern[excludeRegexs.length];
for (int i = 0; i < excludeRegexs.length; i++) {
excludePatterns[i] = Pattern.compile(excludeRegexs[i]);
}
}
/**
* Generates custom assertions for classes in given packages with the Assertions class entry point in given
* destination dir.
*
* @param inputPackages the packages containing the classes we want to generate Assert classes for.
* @param inputClassNames the packages containing the classes we want to generate Assert classes for.
* @param destDir the base directory where the classes are going to be generated.
* @param entryPointFilePackage the package of the assertions entry point class, may be <code>null</code>.
* @throws IOException if the files can't be generated
*/
@SuppressWarnings("unchecked")
public AssertionsGeneratorReport generateAssertionsFor(String[] inputPackages, String[] inputClassNames,
String destDir,
String entryPointFilePackage, boolean hierarchical,
Templates userTemplates) {
generator.setDirectoryWhereAssertionFilesAreGenerated(destDir);
AssertionsGeneratorReport report = new AssertionsGeneratorReport();
report.setDirectoryPathWhereAssertionFilesAreGenerated(destDir);
registerUserTemplates(userTemplates, report);
Set<ClassDescription> classDescriptions = new HashSet<ClassDescription>();
report.setInputPackages(inputPackages);
report.setInputClasses(inputClassNames);
try {
Set<Class<?>> classes = collectClasses(classLoader, addAll(inputPackages, inputClassNames));
report.reportInputClassesNotFound(classes, inputClassNames);
Set<Class<?>> filteredClasses = removeAssertClasses(classes);
removeClassesAccordingToIncludeAndExcludePatterns(filteredClasses);
report.setExcludedClassesFromAssertionGeneration(subtract(classes, filteredClasses));
if (hierarchical) {
for (Class<?> clazz : filteredClasses) {
ClassDescription classDescription = converter.convertToClassDescription(clazz);
File[] generatedCustomAssertionFiles = generator.generateHierarchicalCustomAssertionFor(classDescription,
filteredClasses);
report.addGeneratedAssertionFile(generatedCustomAssertionFiles[0]);
report.addGeneratedAssertionFile(generatedCustomAssertionFiles[1]);
classDescriptions.add(classDescription);
}
} else {
for (Class<?> clazz : filteredClasses) {
ClassDescription classDescription = converter.convertToClassDescription(clazz);
File generatedCustomAssertionFile = generator.generateCustomAssertionFor(classDescription);
report.addGeneratedAssertionFile(generatedCustomAssertionFile);
classDescriptions.add(classDescription);
}
}
for (AssertionsEntryPointType assertionsEntryPointType : assertionsEntryPointToGenerate) {
File assertionsEntryPointFile = generator.generateAssertionsEntryPointClassFor(classDescriptions,
assertionsEntryPointType,
entryPointFilePackage);
report.reportEntryPointGeneration(assertionsEntryPointType, assertionsEntryPointFile);
}
} catch (Exception e) {
report.setException(e);
}
return report;
}
private void registerUserTemplates(Templates userTemplates, AssertionsGeneratorReport report) {
if (userTemplates == null) return;
for (Template template : userTemplates.getTemplates(report)) {
generator.register(template);
}
}
private void removeClassesAccordingToIncludeAndExcludePatterns(Set<Class<?>> filteredClasses) {
for (Iterator<Class<?>> it = filteredClasses.iterator(); it.hasNext();) {
Class<?> element = it.next();
if (!isIncluded(element) || isExcluded(element)) it.remove();
}
}
private boolean isIncluded(Class<?> element) {
String className = element.getName();
for (Pattern includePattern : includePatterns) {
if (includePattern.matcher(className).matches()) return true;
}
log.debug("Won't generate assertions for " + className + " as it does not match any include regex.");
return false;
}
private boolean isExcluded(Class<?> element) {
String className = element.getName();
for (Pattern excludePattern : excludePatterns) {
if (excludePattern.matcher(className).matches()) {
log.debug("Won't generate assertions for " + className + " as it matches exclude regex : " + excludePattern);
return true;
}
}
return false;
}
private Set<Class<?>> removeAssertClasses(Set<Class<?>> classList) {
Set<Class<?>> filteredClassList = newLinkedHashSet();
for (Class<?> clazz : classList) {
String classSimpleName = clazz.getSimpleName();
if (!classSimpleName.endsWith("Assert") && !classSimpleName.endsWith("Assertions")) {
filteredClassList.add(clazz);
}
}
return filteredClassList;
}
@VisibleForTesting
public void setBaseGenerator(BaseAssertionGenerator generator) {
this.generator = generator;
}
public void setLog(Log log) {
this.log = log;
}
public void enableEntryPointClassesGenerationFor(AssertionsEntryPointType type) {
this.assertionsEntryPointToGenerate.add(type);
}
}