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