001/* 002 * SonarQube 003 * Copyright (C) 2009-2017 SonarSource SA 004 * mailto:info AT sonarsource DOT com 005 * 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public License 017 * along with this program; if not, write to the Free Software Foundation, 018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 019 */ 020package org.sonar.api.server.rule; 021 022import java.lang.reflect.Field; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.List; 026import java.util.Map; 027import javax.annotation.CheckForNull; 028import org.apache.commons.lang.StringUtils; 029import org.sonar.api.rule.RuleStatus; 030import org.sonar.api.utils.AnnotationUtils; 031import org.sonar.api.utils.FieldUtils2; 032import org.sonar.api.utils.log.Logger; 033import org.sonar.api.utils.log.Loggers; 034import org.sonar.check.Cardinality; 035 036/** 037 * Read definitions of rules based on the annotations provided by sonar-check-api. It is used 038 * to feed {@link RulesDefinition}. 039 * 040 * @see org.sonar.api.server.rule.RulesDefinition 041 * @since 4.3 042 */ 043public class RulesDefinitionAnnotationLoader { 044 045 private static final Logger LOG = Loggers.get(RulesDefinitionAnnotationLoader.class); 046 047 private static final Map<Class<?>, RuleParamType> TYPE_FOR_CLASS; 048 049 static { 050 Map<Class<?>, RuleParamType> map = new HashMap<>(); 051 map.put(Integer.class, RuleParamType.INTEGER); 052 map.put(int.class, RuleParamType.INTEGER); 053 map.put(Float.class, RuleParamType.FLOAT); 054 map.put(float.class, RuleParamType.FLOAT); 055 map.put(Boolean.class, RuleParamType.BOOLEAN); 056 map.put(boolean.class, RuleParamType.BOOLEAN); 057 TYPE_FOR_CLASS = Collections.unmodifiableMap(map); 058 } 059 060 public void load(RulesDefinition.NewExtendedRepository repo, Class... annotatedClasses) { 061 for (Class annotatedClass : annotatedClasses) { 062 loadRule(repo, annotatedClass); 063 } 064 } 065 066 @CheckForNull 067 RulesDefinition.NewRule loadRule(RulesDefinition.NewExtendedRepository repo, Class clazz) { 068 org.sonar.check.Rule ruleAnnotation = AnnotationUtils.getAnnotation(clazz, org.sonar.check.Rule.class); 069 if (ruleAnnotation != null) { 070 return loadRule(repo, clazz, ruleAnnotation); 071 } else { 072 LOG.warn("The class " + clazz.getCanonicalName() + " should be annotated with " + org.sonar.check.Rule.class); 073 return null; 074 } 075 } 076 077 private static RulesDefinition.NewRule loadRule(RulesDefinition.NewExtendedRepository repo, Class clazz, org.sonar.check.Rule ruleAnnotation) { 078 String ruleKey = StringUtils.defaultIfEmpty(ruleAnnotation.key(), clazz.getCanonicalName()); 079 String ruleName = StringUtils.defaultIfEmpty(ruleAnnotation.name(), null); 080 String description = StringUtils.defaultIfEmpty(ruleAnnotation.description(), null); 081 082 RulesDefinition.NewRule rule = repo.createRule(ruleKey); 083 rule.setName(ruleName).setHtmlDescription(description); 084 rule.setSeverity(ruleAnnotation.priority().name()); 085 rule.setTemplate(ruleAnnotation.cardinality() == Cardinality.MULTIPLE); 086 rule.setStatus(RuleStatus.valueOf(ruleAnnotation.status())); 087 rule.setTags(ruleAnnotation.tags()); 088 089 List<Field> fields = FieldUtils2.getFields(clazz, true); 090 for (Field field : fields) { 091 loadParameters(rule, field); 092 } 093 094 return rule; 095 } 096 097 private static void loadParameters(RulesDefinition.NewRule rule, Field field) { 098 org.sonar.check.RuleProperty propertyAnnotation = field.getAnnotation(org.sonar.check.RuleProperty.class); 099 if (propertyAnnotation != null) { 100 String fieldKey = StringUtils.defaultIfEmpty(propertyAnnotation.key(), field.getName()); 101 RulesDefinition.NewParam param = rule.createParam(fieldKey) 102 .setDescription(propertyAnnotation.description()) 103 .setDefaultValue(propertyAnnotation.defaultValue()); 104 105 if (!StringUtils.isBlank(propertyAnnotation.type())) { 106 try { 107 param.setType(RuleParamType.parse(propertyAnnotation.type().trim())); 108 } catch (IllegalArgumentException e) { 109 throw new IllegalArgumentException("Invalid property type [" + propertyAnnotation.type() + "]", e); 110 } 111 } else { 112 param.setType(guessType(field.getType())); 113 } 114 } 115 } 116 117 static RuleParamType guessType(Class<?> type) { 118 RuleParamType result = TYPE_FOR_CLASS.get(type); 119 return result != null ? result : RuleParamType.STRING; 120 } 121}