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