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