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