123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- /*
- * SonarQube
- * Copyright (C) 2009-2021 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
- package org.sonar.api.batch.rule;
-
- import java.lang.reflect.Field;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.HashMap;
- import java.util.IdentityHashMap;
- import java.util.List;
- import java.util.Map;
- import javax.annotation.CheckForNull;
- import org.apache.commons.lang.StringUtils;
- import org.sonar.api.rule.RuleKey;
- import org.sonar.api.utils.AnnotationUtils;
- import org.sonar.api.utils.FieldUtils2;
- import org.sonar.api.utils.SonarException;
- import org.sonar.check.RuleProperty;
-
- /**
- * Instantiates checks (objects that provide implementation of coding
- * rules) that use sonar-check-api annotations. Checks are selected and configured
- * from the Quality profiles enabled on the current module.
- * <br>
- * Example of check class:
- * <pre>
- * {@literal @}org.sonar.check.Rule(key = "S001")
- * public class CheckS001 {
- * {@literal @}org.sonar.check.RuleProperty
- * private String pattern;
- *
- * public String getPattern() {
- * return pattern;
- * }
- * }
- * </pre>
- * How to use:
- * <pre>
- * public class MyRuleEngine extends BatchExtension {
- * private final CheckFactory checkFactory;
- *
- * public MyRuleEngine(CheckFactory checkFactory) {
- * this.checkFactory = checkFactory;
- * }
- *
- * public void execute() {
- * Checks checks = checkFactory.create("my-rule-repository");
- * checks.addAnnotatedChecks(CheckS001.class);
- * // checks.all() contains an instance of CheckS001
- * // with field "pattern" set to the value specified in
- * // the Quality profile
- *
- * // Checks are used to detect issues on source code
- *
- * // checks.ruleKey(obj) can be used to create the related issues
- * }
- * }
- * </pre>
- * <br>
- * It replaces org.sonar.api.checks.AnnotationCheckFactory
- *
- * @since 4.2
- */
- public class Checks<C> {
- private final ActiveRules activeRules;
- private final String repository;
- private final Map<RuleKey, C> checkByRule = new HashMap<>();
- private final Map<C, RuleKey> ruleByCheck = new IdentityHashMap<>();
-
- Checks(ActiveRules activeRules, String repository) {
- this.activeRules = activeRules;
- this.repository = repository;
- }
-
- @CheckForNull
- public C of(RuleKey ruleKey) {
- return checkByRule.get(ruleKey);
- }
-
- public Collection<C> all() {
- return checkByRule.values();
- }
-
- @CheckForNull
- public RuleKey ruleKey(C check) {
- return ruleByCheck.get(check);
- }
-
- private void add(RuleKey ruleKey, C obj) {
- checkByRule.put(ruleKey, obj);
- ruleByCheck.put(obj, ruleKey);
- }
-
- public Checks<C> addAnnotatedChecks(Object... checkClassesOrObjects) {
- return addAnnotatedChecks((Iterable) Arrays.asList(checkClassesOrObjects));
- }
-
- public Checks<C> addAnnotatedChecks(Iterable checkClassesOrObjects) {
- Map<String, Object> checksByEngineKey = new HashMap<>();
- for (Object checkClassesOrObject : checkClassesOrObjects) {
- String engineKey = annotatedEngineKey(checkClassesOrObject);
- if (engineKey != null) {
- checksByEngineKey.put(engineKey, checkClassesOrObject);
- }
- }
-
- for (ActiveRule activeRule : activeRules.findByRepository(repository)) {
- String engineKey = StringUtils.defaultIfBlank(activeRule.templateRuleKey(), activeRule.ruleKey().rule());
- Object checkClassesOrObject = checksByEngineKey.get(engineKey);
- if (checkClassesOrObject != null) {
- Object obj = instantiate(activeRule, checkClassesOrObject);
- add(activeRule.ruleKey(), (C) obj);
- }
- }
- return this;
- }
-
- private static String annotatedEngineKey(Object annotatedClassOrObject) {
- String key = null;
- org.sonar.check.Rule ruleAnnotation = AnnotationUtils.getAnnotation(annotatedClassOrObject, org.sonar.check.Rule.class);
- if (ruleAnnotation != null) {
- key = ruleAnnotation.key();
- }
- Class clazz = annotatedClassOrObject.getClass();
- if (annotatedClassOrObject instanceof Class) {
- clazz = (Class) annotatedClassOrObject;
- }
- return StringUtils.defaultIfEmpty(key, clazz.getCanonicalName());
- }
-
- private static Object instantiate(ActiveRule activeRule, Object checkClassOrInstance) {
- try {
- Object check = checkClassOrInstance;
- if (check instanceof Class) {
- check = ((Class) checkClassOrInstance).newInstance();
- }
- configureFields(activeRule, check);
- return check;
- } catch (InstantiationException | IllegalAccessException e) {
- throw failToInstantiateCheck(activeRule, checkClassOrInstance, e);
- }
- }
-
- private static RuntimeException failToInstantiateCheck(ActiveRule activeRule, Object checkClassOrInstance, Exception e) {
- throw new IllegalStateException(String.format("Fail to instantiate class %s for rule %s", checkClassOrInstance, activeRule.ruleKey()), e);
- }
-
- private static void configureFields(ActiveRule activeRule, Object check) {
- for (Map.Entry<String, String> param : activeRule.params().entrySet()) {
- Field field = getField(check, param.getKey());
- if (field == null) {
- throw new IllegalStateException(
- String.format("The field '%s' does not exist or is not annotated with @RuleProperty in the class %s", param.getKey(), check.getClass().getName()));
- }
- if (StringUtils.isNotBlank(param.getValue())) {
- configureField(check, field, param.getValue());
- }
- }
- }
-
- @CheckForNull
- private static Field getField(Object check, String key) {
- List<Field> fields = FieldUtils2.getFields(check.getClass(), true);
- for (Field field : fields) {
- RuleProperty propertyAnnotation = field.getAnnotation(RuleProperty.class);
- if (propertyAnnotation != null && (StringUtils.equals(key, field.getName()) || StringUtils.equals(key, propertyAnnotation.key()))) {
- return field;
- }
- }
- return null;
- }
-
- private static void configureField(Object check, Field field, String value) {
- try {
- field.setAccessible(true);
-
- if (field.getType().equals(String.class)) {
- field.set(check, value);
-
- } else if (int.class == field.getType()) {
- field.setInt(check, Integer.parseInt(value));
-
- } else if (short.class == field.getType()) {
- field.setShort(check, Short.parseShort(value));
-
- } else if (long.class == field.getType()) {
- field.setLong(check, Long.parseLong(value));
-
- } else if (double.class == field.getType()) {
- field.setDouble(check, Double.parseDouble(value));
-
- } else if (boolean.class == field.getType()) {
- field.setBoolean(check, Boolean.parseBoolean(value));
-
- } else if (byte.class == field.getType()) {
- field.setByte(check, Byte.parseByte(value));
-
- } else if (Integer.class == field.getType()) {
- field.set(check, Integer.parseInt(value));
-
- } else if (Long.class == field.getType()) {
- field.set(check, Long.parseLong(value));
-
- } else if (Double.class == field.getType()) {
- field.set(check, Double.parseDouble(value));
-
- } else if (Boolean.class == field.getType()) {
- field.set(check, Boolean.parseBoolean(value));
-
- } else {
- throw new SonarException("The type of the field " + field + " is not supported: " + field.getType());
- }
- } catch (IllegalAccessException e) {
- throw new SonarException("Can not set the value of the field " + field + " in the class: " + check.getClass().getName(), e);
- }
- }
- }
|