001 /* 002 * SonarQube, open source software quality management tool. 003 * Copyright (C) 2008-2014 SonarSource 004 * mailto:contact AT sonarsource DOT com 005 * 006 * SonarQube 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 * SonarQube 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 */ 020 package org.sonar.api.server.rule; 021 022 import com.google.common.annotations.VisibleForTesting; 023 import com.google.common.base.Function; 024 import com.google.common.base.Functions; 025 import com.google.common.collect.ImmutableMap; 026 import org.apache.commons.lang.StringUtils; 027 import org.slf4j.Logger; 028 import org.slf4j.LoggerFactory; 029 import org.sonar.api.rule.RuleStatus; 030 import org.sonar.api.utils.AnnotationUtils; 031 import org.sonar.api.utils.FieldUtils2; 032 import org.sonar.check.Cardinality; 033 034 import javax.annotation.CheckForNull; 035 import java.lang.reflect.Field; 036 import java.util.List; 037 038 /** 039 * Read definitions of rules based on the annotations provided by sonar-check-api. It is used 040 * to feed {@link RulesDefinition}. 041 * 042 * @see org.sonar.api.server.rule.RulesDefinition 043 * @since 4.3 044 */ 045 public class RulesDefinitionAnnotationLoader { 046 047 private static final Logger LOG = LoggerFactory.getLogger(RulesDefinitionAnnotationLoader.class); 048 049 private static final Function<Class<?>, RuleParamType> TYPE_FOR_CLASS = Functions.forMap( 050 ImmutableMap.<Class<?>, RuleParamType>builder() 051 .put(Integer.class, RuleParamType.INTEGER) 052 .put(int.class, RuleParamType.INTEGER) 053 .put(Float.class, RuleParamType.FLOAT) 054 .put(float.class, RuleParamType.FLOAT) 055 .put(Boolean.class, RuleParamType.BOOLEAN) 056 .put(boolean.class, RuleParamType.BOOLEAN) 057 .build(), 058 RuleParamType.STRING 059 ); 060 061 public void load(RulesDefinition.NewExtendedRepository repo, Class... annotatedClasses) { 062 for (Class annotatedClass : annotatedClasses) { 063 loadRule(repo, annotatedClass); 064 } 065 } 066 067 @CheckForNull 068 RulesDefinition.NewRule loadRule(RulesDefinition.NewExtendedRepository repo, Class clazz) { 069 org.sonar.check.Rule ruleAnnotation = AnnotationUtils.getAnnotation(clazz, org.sonar.check.Rule.class); 070 if (ruleAnnotation != null) { 071 return loadRule(repo, clazz, ruleAnnotation); 072 } else { 073 LOG.warn("The class " + clazz.getCanonicalName() + " should be annotated with " + org.sonar.check.Rule.class); 074 return null; 075 } 076 } 077 078 private RulesDefinition.NewRule loadRule(RulesDefinition.NewExtendedRepository repo, Class clazz, org.sonar.check.Rule ruleAnnotation) { 079 String ruleKey = StringUtils.defaultIfEmpty(ruleAnnotation.key(), clazz.getCanonicalName()); 080 String ruleName = StringUtils.defaultIfEmpty(ruleAnnotation.name(), null); 081 String description = StringUtils.defaultIfEmpty(ruleAnnotation.description(), null); 082 083 RulesDefinition.NewRule rule = repo.createRule(ruleKey); 084 rule.setName(ruleName).setHtmlDescription(description); 085 rule.setSeverity(ruleAnnotation.priority().name()); 086 rule.setTemplate(ruleAnnotation.cardinality() == Cardinality.MULTIPLE); 087 rule.setStatus(RuleStatus.valueOf(ruleAnnotation.status())); 088 rule.setTags(ruleAnnotation.tags()); 089 090 List<Field> fields = FieldUtils2.getFields(clazz, true); 091 for (Field field : fields) { 092 loadParameters(rule, field); 093 } 094 095 return rule; 096 } 097 098 private void loadParameters(RulesDefinition.NewRule rule, Field field) { 099 org.sonar.check.RuleProperty propertyAnnotation = field.getAnnotation(org.sonar.check.RuleProperty.class); 100 if (propertyAnnotation != null) { 101 String fieldKey = StringUtils.defaultIfEmpty(propertyAnnotation.key(), field.getName()); 102 RulesDefinition.NewParam param = rule.createParam(fieldKey) 103 .setDescription(propertyAnnotation.description()) 104 .setDefaultValue(propertyAnnotation.defaultValue()); 105 106 if (!StringUtils.isBlank(propertyAnnotation.type())) { 107 try { 108 param.setType(RuleParamType.parse(propertyAnnotation.type().trim())); 109 } catch (IllegalArgumentException e) { 110 throw new IllegalArgumentException("Invalid property type [" + propertyAnnotation.type() + "]", e); 111 } 112 } else { 113 param.setType(guessType(field.getType())); 114 } 115 } 116 } 117 118 @VisibleForTesting 119 static RuleParamType guessType(Class<?> type) { 120 return TYPE_FOR_CLASS.apply(type); 121 } 122 }