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