001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar 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 * Sonar 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
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
019 */
020 package org.sonar.api.checks;
021
022 import com.google.common.collect.Maps;
023 import org.apache.commons.lang.StringUtils;
024 import org.sonar.api.profiles.RulesProfile;
025 import org.sonar.api.rules.ActiveRule;
026 import org.sonar.api.rules.ActiveRuleParam;
027 import org.sonar.api.utils.FieldUtils2;
028 import org.sonar.api.utils.SonarException;
029 import org.sonar.check.Check;
030 import org.sonar.check.CheckProperty;
031 import org.sonar.check.Rule;
032 import org.sonar.check.RuleProperty;
033
034 import java.lang.reflect.Field;
035 import java.util.Collection;
036 import java.util.List;
037 import java.util.Map;
038
039 /**
040 * @since 2.3
041 */
042 public final class AnnotationCheckFactory extends CheckFactory {
043
044 private Map<String, Class> checkClassesByKey = Maps.newHashMap();
045
046 private AnnotationCheckFactory(RulesProfile profile, String repositoryKey, Collection<Class> checkClasses) {
047 super(profile, repositoryKey);
048 groupClassesByKey(checkClasses);
049 }
050
051 public static AnnotationCheckFactory create(RulesProfile profile, String repositoryKey, Collection<Class> checkClasses) {
052 AnnotationCheckFactory factory = new AnnotationCheckFactory(profile, repositoryKey, checkClasses);
053 factory.init();
054 return factory;
055 }
056
057 private void groupClassesByKey(Collection<Class> checkClasses) {
058 for (Class checkClass : checkClasses) {
059 String key = getRuleKey(checkClass);
060 if (key != null) {
061 checkClassesByKey.put(key, checkClass);
062 }
063 }
064 }
065
066 protected Object createCheck(ActiveRule activeRule) {
067 Class clazz = checkClassesByKey.get(activeRule.getConfigKey());
068 if (clazz != null) {
069 return instantiate(activeRule, clazz);
070 }
071 return null;
072 }
073
074 private Object instantiate(ActiveRule activeRule, Class clazz) {
075 try {
076 Object check = clazz.newInstance();
077 configureFields(activeRule, check);
078 return check;
079
080 } catch (InstantiationException e) {
081 throw new SonarException("Can not instantiate the check related to the rule " + activeRule, e);
082
083 } catch (IllegalAccessException e) {
084 throw new SonarException("Can not instantiate the check related to the rule " + activeRule, e);
085 }
086 }
087
088 private void configureFields(ActiveRule activeRule, Object check) {
089 for (ActiveRuleParam param : activeRule.getActiveRuleParams()) {
090 Field field = getField(check, param.getKey());
091 if (field == null) {
092 throw new SonarException("The field " + param.getKey() + " does not exist or is not annotated with @RuleProperty in the class " + check.getClass().getName());
093 }
094 if (StringUtils.isNotBlank(param.getValue())) {
095 configureField(check, field, param.getValue());
096 }
097 }
098
099 }
100
101 private void configureField(Object check, Field field, String value) {
102 try {
103 field.setAccessible(true);
104
105 if (field.getType().equals(String.class)) {
106 field.set(check, value);
107
108 } else if ("int".equals(field.getType().getSimpleName())) {
109 field.setInt(check, Integer.parseInt(value));
110
111 } else if ("short".equals(field.getType().getSimpleName())) {
112 field.setShort(check, Short.parseShort(value));
113
114 } else if ("long".equals(field.getType().getSimpleName())) {
115 field.setLong(check, Long.parseLong(value));
116
117 } else if ("double".equals(field.getType().getSimpleName())) {
118 field.setDouble(check, Double.parseDouble(value));
119
120 } else if ("boolean".equals(field.getType().getSimpleName())) {
121 field.setBoolean(check, Boolean.parseBoolean(value));
122
123 } else if ("byte".equals(field.getType().getSimpleName())) {
124 field.setByte(check, Byte.parseByte(value));
125
126 } else if (field.getType().equals(Integer.class)) {
127 field.set(check, Integer.parseInt(value));
128
129 } else if (field.getType().equals(Long.class)) {
130 field.set(check, Long.parseLong(value));
131
132 } else if (field.getType().equals(Double.class)) {
133 field.set(check, Double.parseDouble(value));
134
135 } else if (field.getType().equals(Boolean.class)) {
136 field.set(check, Boolean.parseBoolean(value));
137
138 } else {
139 throw new SonarException("The type of the field " + field + " is not supported: " + field.getType());
140 }
141 } catch (IllegalAccessException e) {
142 throw new SonarException("Can not set the value of the field " + field + " in the class: " + check.getClass().getName(), e);
143 }
144 }
145
146 private Field getField(Object check, String key) {
147 List<Field> fields = FieldUtils2.getFields(check.getClass(), true);
148 for (Field field : fields) {
149 RuleProperty propertyAnnotation = field.getAnnotation(RuleProperty.class);
150 if (propertyAnnotation != null) {
151 if (StringUtils.equals(key, field.getName()) || StringUtils.equals(key, propertyAnnotation.key())) {
152 return field;
153 }
154 } else {
155 CheckProperty checkAnnotation = field.getAnnotation(CheckProperty.class);
156 if (checkAnnotation != null) {
157 if (StringUtils.equals(key, field.getName()) || StringUtils.equals(key, checkAnnotation.key())) {
158 return field;
159 }
160 }
161 }
162 }
163 return null;
164 }
165
166 private String getRuleKey(Class annotatedClass) {
167 String key = null;
168 Rule ruleAnnotation = (Rule) annotatedClass.getAnnotation(Rule.class);
169 if (ruleAnnotation != null) {
170 key = ruleAnnotation.key();
171 } else {
172 Check checkAnnotation = (Check) annotatedClass.getAnnotation(Check.class);
173 if (checkAnnotation != null) {
174 key = checkAnnotation.key();
175
176 }
177 }
178 return StringUtils.defaultIfEmpty(key, annotatedClass.getCanonicalName());
179 }
180 }