Skip to content

Commit

Permalink
Issue checkstyle#4916: Adding ability to force load
Browse files Browse the repository at this point in the history
  • Loading branch information
kazachka committed Nov 24, 2017
1 parent 0ce09e7 commit b16944a
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 14 deletions.
Expand Up @@ -49,6 +49,17 @@
* @author lkuehne
*/
public class PackageObjectFactory implements ModuleFactory {

/**
* Enum class to define loading options.
*/
public enum ModuleLoadOption {
/** Searching from registred checkstyle modules. **/
SEARCH_REGISTERED_PACKAGES,
/** As SEARCH_REGISTERED_PACKAGES and also searching for class in checkstyle packages. **/
TRY_IN_ALL_REGISTERED_PACKAGES,
}

/** Base package of checkstyle modules checks. */
public static final String BASE_PACKAGE = "com.puppycrawl.tools.checkstyle";

Expand Down Expand Up @@ -87,6 +98,9 @@ public class PackageObjectFactory implements ModuleFactory {
/** Map of third party Checkstyle module names to the set of their fully qualified names. */
private Map<String, Set<String>> thirdPartyNameToFullModuleNames;

/** If true old style force loading will be used. Required for eclipse-cs plugin.*/
private ModuleLoadOption moduleLoadOption;

static {
fillShortToFullModuleNamesMap();
}
Expand All @@ -98,6 +112,18 @@ public class PackageObjectFactory implements ModuleFactory {
* core and custom modules
*/
public PackageObjectFactory(Set<String> packageNames, ClassLoader moduleClassLoader) {
this(packageNames, moduleClassLoader, ModuleLoadOption.SEARCH_REGISTERED_PACKAGES);
}

/**
* Creates a new {@code PackageObjectFactory} instance.
* @param packageNames the list of package names to use
* @param moduleClassLoader class loader used to load Checkstyle
* core and custom modules
* @param moduleLoadOption loading option
*/
public PackageObjectFactory(Set<String> packageNames, ClassLoader moduleClassLoader,
ModuleLoadOption moduleLoadOption) {
if (moduleClassLoader == null) {
throw new IllegalArgumentException(NULL_LOADER_MESSAGE);
}
Expand All @@ -108,6 +134,7 @@ public PackageObjectFactory(Set<String> packageNames, ClassLoader moduleClassLoa
//create a copy of the given set, but retain ordering
packages = new LinkedHashSet<>(packageNames);
this.moduleClassLoader = moduleClassLoader;
this.moduleLoadOption = moduleLoadOption;
}

/**
Expand Down Expand Up @@ -144,19 +171,7 @@ public Object createModule(String name) throws CheckstyleException {
Object instance = null;
// if the name is a simple class name, try to find it in maps at first
if (!name.contains(PACKAGE_SEPARATOR)) {
// find the name in hardcode map
final String fullModuleName = NAME_TO_FULL_MODULE_NAME.get(name);
if (fullModuleName == null) {
final String fullCheckModuleName =
NAME_TO_FULL_MODULE_NAME.get(name + CHECK_SUFFIX);
if (fullCheckModuleName != null) {
instance = createObject(fullCheckModuleName);
}
}
else {
instance = createObject(fullModuleName);
}

instance = createFromStandardCheckSet(name);
// find the name in third party map
if (instance == null) {
if (thirdPartyNameToFullModuleNames == null) {
Expand All @@ -166,10 +181,13 @@ public Object createModule(String name) throws CheckstyleException {
instance = createObjectFromMap(name, thirdPartyNameToFullModuleNames);
}
}

if (instance == null) {
instance = createObject(name);
}
if (instance == null
&& moduleLoadOption == ModuleLoadOption.TRY_IN_ALL_REGISTERED_PACKAGES) {
instance = createModuleByTryInEachPackage(name);
}
if (instance == null) {
String attemptedNames = null;
if (!name.contains(PACKAGE_SEPARATOR)) {
Expand All @@ -186,6 +204,28 @@ public Object createModule(String name) throws CheckstyleException {
return instance;
}

/**
* Create object from one of Checkstyle module names.
* @param name name of module.
* @return instance of module.
* @throws CheckstyleException if the class fails to instantiate or there are ambiguous classes.
*/
private Object createFromStandardCheckSet(String name) throws CheckstyleException {
final String fullModuleName = NAME_TO_FULL_MODULE_NAME.get(name);
Object instance = null;
if (fullModuleName == null) {
final String fullCheckModuleName =
NAME_TO_FULL_MODULE_NAME.get(name + CHECK_SUFFIX);
if (fullCheckModuleName != null) {
instance = createObject(fullCheckModuleName);
}
}
else {
instance = createObject(fullModuleName);
}
return instance;
}

/**
* Create object with the help of the supplied map.
* @param name name of module.
Expand Down Expand Up @@ -307,6 +347,30 @@ private Object createObject(String className) throws CheckstyleException {
return instance;
}

/**
* Searching to class with given name (or name concatinated with &quot;Check&quot;) in existing
* packages. Returns instance if class found or, otherwise, null.
* @param name the name of a class.
* @return the {@code Object} created by loader.
* @throws CheckstyleException if an error occurs.
*/
private Object createModuleByTryInEachPackage(String name) throws CheckstyleException {
final Set<String> possibleNames = packages.stream()
.map(packageName -> packageName + PACKAGE_SEPARATOR + name)
.collect(Collectors.toSet());
possibleNames.addAll(possibleNames.stream()
.map(possibleName -> possibleName + CHECK_SUFFIX)
.collect(Collectors.toSet()));
Object instance = null;
for (String possibleName : possibleNames) {
instance = createObject(possibleName);
if (instance != null) {
break;
}
}
return instance;
}

/**
* Fill short-to-full module names map.
*/
Expand Down Expand Up @@ -769,4 +833,5 @@ private static void fillModulesFromCheckstylePackage() {
NAME_TO_FULL_MODULE_NAME.put("Checker", BASE_PACKAGE + ".Checker");
NAME_TO_FULL_MODULE_NAME.put("TreeWalker", BASE_PACKAGE + ".TreeWalker");
}

}
Expand Up @@ -22,6 +22,7 @@
import static com.puppycrawl.tools.checkstyle.PackageObjectFactory.AMBIGUOUS_MODULE_NAME_EXCEPTION_MESSAGE;
import static com.puppycrawl.tools.checkstyle.PackageObjectFactory.BASE_PACKAGE;
import static com.puppycrawl.tools.checkstyle.PackageObjectFactory.CHECK_SUFFIX;
import static com.puppycrawl.tools.checkstyle.PackageObjectFactory.ModuleLoadOption.TRY_IN_ALL_REGISTERED_PACKAGES;
import static com.puppycrawl.tools.checkstyle.PackageObjectFactory.NULL_LOADER_MESSAGE;
import static com.puppycrawl.tools.checkstyle.PackageObjectFactory.NULL_PACKAGE_MESSAGE;
import static com.puppycrawl.tools.checkstyle.PackageObjectFactory.PACKAGE_SEPARATOR;
Expand Down Expand Up @@ -53,6 +54,7 @@
import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
import com.puppycrawl.tools.checkstyle.api.FileText;
import com.puppycrawl.tools.checkstyle.api.LocalizedMessage;
import com.puppycrawl.tools.checkstyle.checks.annotation.AnnotationLocationCheck;
import com.puppycrawl.tools.checkstyle.internal.utils.CheckUtil;

/**
Expand Down Expand Up @@ -111,6 +113,19 @@ public void testCtorNullPackageException2() {
}
}

@Test
public void testCtorNullPackageException3() {
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
final Object test = new PackageObjectFactory(Collections.singleton(null), classLoader,
TRY_IN_ALL_REGISTERED_PACKAGES);
fail("Exception is expected but got " + test);
}
catch (IllegalArgumentException ex) {
assertEquals("Invalid exception message", NULL_PACKAGE_MESSAGE, ex.getMessage());
}
}

@Test
public void testMakeObjectFromName()
throws CheckstyleException {
Expand Down Expand Up @@ -224,6 +239,69 @@ public void testCreateObjectFromFullModuleNamesWithCantInstantiateException() {
}
}

@Test
public void testCreateObjectFromFullModuleNamesWithExceptionByBruteForce() {
final String package1 = BASE_PACKAGE + ".wrong1";
final String package2 = BASE_PACKAGE + ".wrong2";
final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
final PackageObjectFactory objectFactory = new PackageObjectFactory(
new LinkedHashSet<>(Arrays.asList(package1, package2)), classLoader,
TRY_IN_ALL_REGISTERED_PACKAGES);
final String name = "FooCheck";
final String checkName = name + CHECK_SUFFIX;
try {
objectFactory.createModule(name);
fail("Exception is expected");
}
catch (CheckstyleException ex) {
final String attemptedNames = package1 + PACKAGE_SEPARATOR + name + STRING_SEPARATOR
+ package2 + PACKAGE_SEPARATOR + name + STRING_SEPARATOR
+ checkName + STRING_SEPARATOR
+ package1 + PACKAGE_SEPARATOR + checkName + STRING_SEPARATOR
+ package2 + PACKAGE_SEPARATOR + checkName;
final LocalizedMessage exceptionMessage = new LocalizedMessage(0,
Definitions.CHECKSTYLE_BUNDLE, UNABLE_TO_INSTANTIATE_EXCEPTION_MESSAGE,
new String[] {name, attemptedNames}, null, getClass(), null);
assertEquals("Invalid exception message",
exceptionMessage.getMessage(), ex.getMessage());
}
}

@Test
public void testCreateObjectByBruteForce() throws Exception {
final String className = "Checker";
final Method createModuleByBruteForce = PackageObjectFactory.class.getDeclaredMethod(
"createModuleByTryInEachPackage", String.class);
createModuleByBruteForce.setAccessible(true);
final Checker checker = (Checker) createModuleByBruteForce.invoke(factory, className);
assertNotNull("Checker should not be null when creating module from name", checker);
}

@Test
public void testCreateCheckByBruteForce() throws Exception {
final String checkName = "AnnotationLocation";
final Method createModuleByBruteForce = PackageObjectFactory.class.getDeclaredMethod(
"createModuleByTryInEachPackage", String.class);
final PackageObjectFactory packageObjectFactory = new PackageObjectFactory(
new HashSet<>(Arrays.asList(BASE_PACKAGE, BASE_PACKAGE + ".checks.annotation")),
Thread.currentThread().getContextClassLoader(), TRY_IN_ALL_REGISTERED_PACKAGES);
createModuleByBruteForce.setAccessible(true);
final AnnotationLocationCheck check = (AnnotationLocationCheck) createModuleByBruteForce
.invoke(packageObjectFactory, checkName);
assertNotNull("Check should not be null when creating module from name", check);
}

@Test
public void testCreateCheckWithPartialPackageNameByBruteForce() throws Exception {
final String checkName = "checks.annotation.AnnotationLocation";
final PackageObjectFactory packageObjectFactory = new PackageObjectFactory(
new HashSet<>(Collections.singletonList(BASE_PACKAGE)),
Thread.currentThread().getContextClassLoader(), TRY_IN_ALL_REGISTERED_PACKAGES);
final AnnotationLocationCheck check = (AnnotationLocationCheck) packageObjectFactory
.createModule(checkName);
assertNotNull("Check should not be null when creating module from name", check);
}

@Test
@SuppressWarnings("unchecked")
public void testGenerateThirdPartyNameToFullModuleNameWithException() throws Exception {
Expand Down

0 comments on commit b16944a

Please sign in to comment.