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 Oct 30, 2017
1 parent dd36ada commit 3ccb887
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 3 deletions.
13 changes: 12 additions & 1 deletion src/main/java/com/puppycrawl/tools/checkstyle/Checker.java
Expand Up @@ -133,6 +133,9 @@ public class Checker extends AutomaticBean implements MessageDispatcher, RootMod
/** Controls whether exceptions should halt execution or not. */
private boolean haltOnException = true;

/** If true old style force loading will be used. */
private boolean forceLoad;

/**
* Creates a new {@code Checker} instance.
* The instance needs to be contextualized and configured.
Expand Down Expand Up @@ -427,7 +430,7 @@ public void finishLocalSetup() throws CheckstyleException {
final Set<String> packageNames = PackageNamesLoader
.getPackageNames(moduleClassLoader);
moduleFactory = new PackageObjectFactory(packageNames,
moduleClassLoader);
moduleClassLoader, forceLoad);
}

final DefaultContext context = new DefaultContext();
Expand Down Expand Up @@ -614,6 +617,14 @@ public void setHaltOnException(boolean haltOnException) {
this.haltOnException = haltOnException;
}

/**
* Sets the field forceLoad.
* @param forceLoad the new value.
*/
public void setForceLoad(boolean forceLoad) {
this.forceLoad = forceLoad;
}

/**
* Clears the cache.
*/
Expand Down
Expand Up @@ -87,6 +87,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. */
private boolean forceLoad;

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

/**
* 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 forceLoad force loading using
*/
public PackageObjectFactory(Set<String> packageNames, ClassLoader moduleClassLoader,
boolean forceLoad) {
if (moduleClassLoader == null) {
throw new IllegalArgumentException(NULL_LOADER_MESSAGE);
}
Expand All @@ -108,6 +123,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.forceLoad = forceLoad;
}

/**
Expand Down Expand Up @@ -166,9 +182,8 @@ public Object createModule(String name) throws CheckstyleException {
instance = createObjectFromMap(name, thirdPartyNameToFullModuleNames);
}
}

if (instance == null) {
instance = createObject(name);
instance = createFromFullName(name);
}
if (instance == null) {
String attemptedNames = null;
Expand Down Expand Up @@ -307,6 +322,46 @@ private Object createObject(String className) throws CheckstyleException {
return instance;
}

/**
* Trying to create class with given full name (or name concatinated with &quot;Check&quot;)
* packages and then to force create (if enabled). Returns instance if class found or,
* otherwise, null.
* @param name the full name of a class.
* @return the {@code Object} created by loader.
* @throws CheckstyleException if an error occurs.
*/
private Object createFromFullName(String name) throws CheckstyleException {
Object instance = createObject(name);
if (instance == null && forceLoad) {
instance = forceCreateModule(name);
}
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 forceCreateModule(String name) throws CheckstyleException {
Object instance = null;
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()));
for (String possibleName : possibleNames) {
instance = createObject(possibleName);
if (instance != null) {
break;
}
}
return instance;
}

/**
* Fill short-to-full module names map.
*/
Expand Down
13 changes: 13 additions & 0 deletions src/test/java/com/puppycrawl/tools/checkstyle/MainTest.java
Expand Up @@ -275,6 +275,19 @@ public void testExistingTargetFile() throws Exception {
getPath("InputMain.java"));
}

@Test
public void testExistingTargetFileWithForceLoad() throws Exception {

exit.checkAssertionAfterwards(() -> {
assertEquals("Unexpected ouput log", auditStartMessage.getMessage() + EOL
+ auditFinishMessage.getMessage() + EOL,
systemOut.getLog());
assertEquals("Unexpected system error log", "", systemErr.getLog());
});
Main.main("-c", getPath("InputMainConfig-forceLoad-classname.xml"),
getPath("InputMain.java"));
}

@Test
public void testExistingTargetFileXmlOutput() throws Exception {

Expand Down
Expand Up @@ -53,6 +53,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 +112,18 @@ public void testCtorNullPackageException2() {
}
}

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

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

@Test
public void testCreateObjectFromFullModuleNamesWithCantInstantiateExceptionWithForceLoad() {
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, true);
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 testCreateObjectWithForceLoad() throws Exception {
final String className = "Checker";
final Method forceCreateModule = PackageObjectFactory.class.getDeclaredMethod(
"forceCreateModule", String.class);
forceCreateModule.setAccessible(true);
final Checker checker = (Checker) forceCreateModule.invoke(factory, className);
assertNotNull(checker);
}

@Test
public void testCreateCheckWithForceLoad() throws Exception {
final String checkName = "AnnotationLocation";
final Method createFromFullName = PackageObjectFactory.class.getDeclaredMethod(
"createFromFullName", String.class);
final PackageObjectFactory packageObjectFactory = new PackageObjectFactory(
new HashSet<>(Arrays.asList(BASE_PACKAGE, BASE_PACKAGE + ".checks.annotation")),
Thread.currentThread().getContextClassLoader(), true);
createFromFullName.setAccessible(true);
final AnnotationLocationCheck check = (AnnotationLocationCheck) createFromFullName.invoke(
packageObjectFactory, checkName);
assertNotNull(check);
}

@Test
@SuppressWarnings("unchecked")
public void testGenerateThirdPartyNameToFullModuleNameWithException() throws Exception {
Expand Down
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.1//EN"
"http://checkstyle.sourceforge.net/dtds/configuration_1_1.dtd">

<module name="Checker">
<property name="forceLoad" value="true"/>
<module name="TreeWalker">
<module name="TypeName">
<property name="severity" value="warning"/>
</module>
</module>
</module>
7 changes: 7 additions & 0 deletions src/xdocs/config.xml
Expand Up @@ -356,6 +356,13 @@
<td><code>true</code></td>
<td>7.4</td>
</tr>
<tr>
<td>forceLoad</td>
<td>If true old style force loading will be used</td>
<td><a href="property_types.html#boolean">Boolean</a></td>
<td><code>false</code></td>
<td>8.5</td>
</tr>
</table>
</subsection>

Expand Down

0 comments on commit 3ccb887

Please sign in to comment.