001//////////////////////////////////////////////////////////////////////////////// 002// checkstyle: Checks Java source code for adherence to a set of rules. 003// Copyright (C) 2001-2016 the original author or authors. 004// 005// This library is free software; you can redistribute it and/or 006// modify it under the terms of the GNU Lesser General Public 007// License as published by the Free Software Foundation; either 008// version 2.1 of the License, or (at your option) any later version. 009// 010// This library is distributed in the hope that it will be useful, 011// but WITHOUT ANY WARRANTY; without even the implied warranty of 012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013// Lesser General Public License for more details. 014// 015// You should have received a copy of the GNU Lesser General Public 016// License along with this library; if not, write to the Free Software 017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 018//////////////////////////////////////////////////////////////////////////////// 019 020package com.puppycrawl.tools.checkstyle; 021 022import java.lang.reflect.Constructor; 023import java.util.Iterator; 024import java.util.Set; 025 026import org.apache.commons.logging.Log; 027import org.apache.commons.logging.LogFactory; 028 029import com.google.common.base.Joiner; 030import com.google.common.collect.Sets; 031import com.puppycrawl.tools.checkstyle.api.CheckstyleException; 032import com.puppycrawl.tools.checkstyle.api.LocalizedMessage; 033 034/** 035 * A factory for creating objects from package names and names. 036 * @author Rick Giles 037 * @author lkuehne 038 */ 039public class PackageObjectFactory implements ModuleFactory { 040 /** Logger for PackageObjectFactory. */ 041 private static final Log LOG = LogFactory.getLog(PackageObjectFactory.class); 042 043 /** Log message when ignoring exception. */ 044 private static final String IGNORING_EXCEPTION_MESSAGE = "Keep looking, ignoring exception"; 045 046 /** Exception message when it is unable to create a class instance. */ 047 private static final String UNABLE_TO_INSTANTIATE_EXCEPTION_MESSAGE = 048 "PackageObjectFactory.unableToInstantiateExceptionMessage"; 049 050 /** Separator to use in strings. */ 051 private static final String STRING_SEPARATOR = ", "; 052 053 /** A list of package names to prepend to class names. */ 054 private final Set<String> packages; 055 /** The class loader used to load Checkstyle core and custom modules. */ 056 private final ClassLoader moduleClassLoader; 057 058 /** 059 * Creates a new {@code PackageObjectFactory} instance. 060 * @param packageNames the list of package names to use 061 * @param moduleClassLoader class loader used to load Checkstyle 062 * core and custom modules 063 */ 064 public PackageObjectFactory(Set<String> packageNames, ClassLoader moduleClassLoader) { 065 if (moduleClassLoader == null) { 066 throw new IllegalArgumentException( 067 "moduleClassLoader must not be null"); 068 } 069 070 //create a copy of the given set, but retain ordering 071 packages = Sets.newLinkedHashSet(packageNames); 072 this.moduleClassLoader = moduleClassLoader; 073 } 074 075 /** 076 * Creates a new instance of a class from a given name, or that name 077 * concatenated with "Check". If the name is 078 * a class name, creates an instance of the named class. Otherwise, creates 079 * an instance of a class name obtained by concatenating the given name 080 * to a package name from a given list of package names. 081 * @param name the name of a class. 082 * @return the {@code Object} created by loader. 083 * @throws CheckstyleException if an error occurs. 084 */ 085 @Override 086 public Object createModule(String name) throws CheckstyleException { 087 Object instance = createObjectWithIgnoringProblems(name, getAllPossibleNames(name)); 088 if (instance == null) { 089 final String nameCheck = name + "Check"; 090 instance = createObjectWithIgnoringProblems(nameCheck, getAllPossibleNames(nameCheck)); 091 if (instance == null) { 092 093 final String attemptedNames = joinPackageNamesWithClassName(name) 094 + STRING_SEPARATOR + nameCheck + STRING_SEPARATOR 095 + joinPackageNamesWithClassName(nameCheck); 096 final LocalizedMessage exceptionMessage = new LocalizedMessage(0, 097 Definitions.CHECKSTYLE_BUNDLE, UNABLE_TO_INSTANTIATE_EXCEPTION_MESSAGE, 098 new String[] {name, attemptedNames}, null, getClass(), null); 099 throw new CheckstyleException(exceptionMessage.getMessage()); 100 } 101 } 102 return instance; 103 } 104 105 /** 106 * Create a new instance of a named class. 107 * @param className the name of the class to instantiate. 108 * @param secondAttempt the set of names to attempt instantiation 109 * if usage of the className was not successful. 110 * @return the {@code Object} created by loader or null. 111 */ 112 private Object createObjectWithIgnoringProblems(String className, 113 Set<String> secondAttempt) { 114 Object instance = createObject(className); 115 if (instance == null) { 116 final Iterator<String> ite = secondAttempt.iterator(); 117 while (instance == null && ite.hasNext()) { 118 instance = createObject(ite.next()); 119 } 120 } 121 return instance; 122 } 123 124 /** 125 * Generate the set of all possible names for a class name. 126 * @param name the name of the class get possible names for. 127 * @return all possible name for a class. 128 */ 129 private Set<String> getAllPossibleNames(String name) { 130 final Set<String> names = Sets.newHashSet(); 131 for (String packageName : packages) { 132 names.add(packageName + name); 133 } 134 return names; 135 } 136 137 /** 138 * Creates a string by joining package names with a class name. 139 * @param className name of the class for joining. 140 * @return a string which is obtained by joining package names with a class name. 141 */ 142 private String joinPackageNamesWithClassName(String className) { 143 final Joiner joiner = Joiner.on(className + STRING_SEPARATOR).skipNulls(); 144 return joiner.join(packages) + className; 145 } 146 147 /** 148 * Creates a new instance of a named class. 149 * @param className the name of the class to instantiate. 150 * @return the {@code Object} created by loader or null. 151 */ 152 private Object createObject(String className) { 153 Object instance = null; 154 try { 155 final Class<?> clazz = Class.forName(className, true, moduleClassLoader); 156 final Constructor<?> declaredConstructor = clazz.getDeclaredConstructor(); 157 declaredConstructor.setAccessible(true); 158 instance = declaredConstructor.newInstance(); 159 } 160 catch (final ReflectiveOperationException | NoClassDefFoundError exception) { 161 LOG.debug(IGNORING_EXCEPTION_MESSAGE, exception); 162 } 163 return instance; 164 } 165}