]> source.dussan.org Git - sonarqube.git/commitdiff
add the package org.sonar.api.checks in order to implement its own rules engine
authorsimonbrandhof <simon.brandhof@gmail.com>
Thu, 23 Sep 2010 13:29:43 +0000 (13:29 +0000)
committersimonbrandhof <simon.brandhof@gmail.com>
Thu, 23 Sep 2010 13:29:43 +0000 (13:29 +0000)
23 files changed:
sonar-check-api/src/main/java/org/sonar/check/AnnotationIntrospector.java
sonar-check-api/src/main/java/org/sonar/check/BelongsToProfiles.java
sonar-check-api/src/main/java/org/sonar/check/Check.java
sonar-check-api/src/main/java/org/sonar/check/CheckProperty.java
sonar-check-api/src/main/java/org/sonar/check/Message.java
sonar-check-api/src/main/java/org/sonar/check/Rule.java [new file with mode: 0644]
sonar-check-api/src/main/java/org/sonar/check/RuleProperty.java [new file with mode: 0644]
sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/AnnotationCheckerFactoryTest.java
sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckWithUnsupportedPropertyType.java
sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckerWithIntegerProperty.java
sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckerWithPrimitiveProperties.java
sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckerWithStringProperty.java
sonar-plugin-api/src/main/java/org/sonar/api/checks/AnnotationCheckFactory.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/checks/CheckFactory.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/rules/AnnotationRuleRepository.java [new file with mode: 0644]
sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesCategory.java
sonar-plugin-api/src/test/java/org/sonar/api/checks/AnnotationCheckFactoryTest.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithIntegerProperty.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithOverriddenPropertyKey.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithPrimitiveProperties.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithStringProperty.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithUnsupportedPropertyType.java [new file with mode: 0644]
sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithoutProperties.java [new file with mode: 0644]

index 15def7431c3b37837fbe7c97bc5868d581374eb2..d368a616240a056a145ec3ba4c21189f7aad7e79 100644 (file)
@@ -23,6 +23,11 @@ import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.List;
 
+/**
+ * @since 2.1 (experimental)
+ * @deprecated since 2.3
+ */
+@Deprecated
 public final class AnnotationIntrospector {
 
   private AnnotationIntrospector() {
index a7d15fdf891423a912d3db427b466f1afa8d0c36..16ada04cce878a6035ad424d128e0db1e47f9a7e 100644 (file)
@@ -25,10 +25,12 @@ import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
 /**
- * @since 2.1
+ * @since 2.1 (experimental)
+ * @deprecated since 2.3. Not supported anymore
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
+@Deprecated
 public @interface BelongsToProfiles {
 
   BelongsToProfile[] value() default {};
index 8a8ae8ef1af9b3a61034f0667722d0d1b570493c..6731d04156001dae976e325201ff32ce0e7fb06d 100644 (file)
@@ -25,10 +25,12 @@ import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
 /**
- * @since 2.1
+ * @since 2.1 (experimental)
+ * @deprecated since 2.3. Use @Rule
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.TYPE)
+@Deprecated
 public @interface Check {
 
   /**
index 17ac5801e5362b78f4adb4065cfc6c9ccf25b86e..935e8e8592d73212cbe36a8768765f2d8cc32d8b 100644 (file)
@@ -25,10 +25,12 @@ import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
 
 /**
- * @since 2.1
+ * @since 2.1 (experimental)
+ * @deprecated since 2.3. Use @RuleProperty
  */
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.FIELD)
+@Deprecated
 public @interface CheckProperty {
 
   /**
index 0eecb1437c2764558015ea346595d9f5a0e61bf4..94bea04c30398fc2f2891d6d129496c6479ecbd9 100644 (file)
@@ -21,6 +21,7 @@ package org.sonar.check;
 
 import java.util.Locale;
 
+@Deprecated
 public interface Message {
 
   /**
diff --git a/sonar-check-api/src/main/java/org/sonar/check/Rule.java b/sonar-check-api/src/main/java/org/sonar/check/Rule.java
new file mode 100644 (file)
index 0000000..2b072cc
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.check;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @since 2.3
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface Rule {
+
+  /**
+   * The default key is the class name.
+   */
+  String key() default "";
+
+  /**
+   * The rule name. If not defined, then the name is the key
+   */
+  String name() default "";
+
+  /**
+   * The description, optional.
+   */
+  String description() default "";
+
+  /**
+   * Default priority.
+   */
+  Priority priority() default Priority.MAJOR;
+
+  /**
+   * Will probably be deprecated and replaced by tags
+   */
+  IsoCategory isoCategory();
+}
diff --git a/sonar-check-api/src/main/java/org/sonar/check/RuleProperty.java b/sonar-check-api/src/main/java/org/sonar/check/RuleProperty.java
new file mode 100644 (file)
index 0000000..fc545a2
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.check;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @since 2.3
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+public @interface RuleProperty {
+
+  /**
+   * The default key is the field name, read by reflection. Overriding this key can be useful when
+   * obfuscating the code.
+   */
+  String key() default "";
+
+  /**
+   * Optional description
+   */
+  String description() default "";
+
+  /**
+   * Optional default value.
+   */
+  String defaultValue() default "";
+}
index 8235725ac568d0e42df9d08c59f54f41d2fab248..8e89d46dd4306e8eac98984bf7decb01f262654a 100644 (file)
@@ -1,3 +1,22 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
 package org.sonar.api.checks.checkers;
 
 import org.junit.Before;
index b5b9f1bacbbee998445a3d9fb3fece9ebbd4c922..3fb27011290ff90893d6a047a5144dc5cdb8eed0 100644 (file)
@@ -5,13 +5,6 @@ import org.sonar.check.CheckProperty;
 import org.sonar.check.IsoCategory;
 import org.sonar.check.Priority;
 
-/**
- * Created by IntelliJ IDEA.
- * User: simonbrandhof
- * Date: Sep 14, 2010
- * Time: 11:20:57 AM
- * To change this template use File | Settings | File Templates.
- */
 @Check(isoCategory = IsoCategory.Efficiency, priority = Priority.CRITICAL)
 class CheckWithUnsupportedPropertyType {
 
index 4ae71746cf357086aa3098d232fc0230dd195930..1c0f3983a6049011787d2b570e5ea9894480183c 100644 (file)
@@ -1,3 +1,22 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
 package org.sonar.api.checks.checkers;
 
 import org.sonar.check.Check;
index 15af2ade92f73b9c33bc5b57e6c4d6e769fe0e20..3bf938f3d9eef9af9f91a2dc3b250afc7babf4f8 100644 (file)
@@ -5,13 +5,6 @@ import org.sonar.check.CheckProperty;
 import org.sonar.check.IsoCategory;
 import org.sonar.check.Priority;
 
-/**
- * Created by IntelliJ IDEA.
- * User: simonbrandhof
- * Date: Sep 14, 2010
- * Time: 11:20:57 AM
- * To change this template use File | Settings | File Templates.
- */
 @Check(isoCategory = IsoCategory.Efficiency, priority = Priority.CRITICAL)
 class CheckerWithPrimitiveProperties {
 
index a7f62a248e35b609e4cb19895b79b49db4613ce8..db40a9f1bc8ee033e38a94facd7b39b461fdf741 100644 (file)
@@ -5,13 +5,6 @@ import org.sonar.check.CheckProperty;
 import org.sonar.check.IsoCategory;
 import org.sonar.check.Priority;
 
-/**
- * Created by IntelliJ IDEA.
- * User: simonbrandhof
- * Date: Sep 14, 2010
- * Time: 11:20:57 AM
- * To change this template use File | Settings | File Templates.
- */
 @Check(isoCategory = IsoCategory.Efficiency, priority = Priority.CRITICAL)
 class CheckerWithStringProperty {
 
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/AnnotationCheckFactory.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/AnnotationCheckFactory.java
new file mode 100644 (file)
index 0000000..918a755
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.api.checks;
+
+import com.google.common.collect.Maps;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.rules.ActiveRule;
+import org.sonar.api.rules.ActiveRuleParam;
+import org.sonar.api.utils.SonarException;
+import org.sonar.check.Check;
+import org.sonar.check.CheckProperty;
+import org.sonar.check.Rule;
+import org.sonar.check.RuleProperty;
+
+import java.lang.reflect.Field;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * @since 2.3
+ */
+public final class AnnotationCheckFactory extends CheckFactory {
+
+  private Map<String, Class> checkClassesByKey = Maps.newHashMap();
+
+  private AnnotationCheckFactory(RulesProfile profile, String repositoryKey, Collection<Class> checkClasses) {
+    super(profile, repositoryKey);
+    groupClassesByKey(checkClasses);
+  }
+
+
+  public static AnnotationCheckFactory create(RulesProfile profile, String repositoryKey, Collection<Class> checkClasses) {
+    AnnotationCheckFactory factory = new AnnotationCheckFactory(profile, repositoryKey, checkClasses);
+    factory.init();
+    return factory;
+  }
+
+  private void groupClassesByKey(Collection<Class> checkClasses) {
+    for (Class checkClass : checkClasses) {
+      String key = getRuleKey(checkClass);
+      if (key != null) {
+        checkClassesByKey.put(key, checkClass);
+      }
+    }
+  }
+
+  protected Object createCheck(ActiveRule activeRule) {
+    Class clazz = checkClassesByKey.get(activeRule.getRuleKey());
+    if (clazz != null) {
+      return instantiate(activeRule, clazz);
+    }
+    return null;
+  }
+
+  private Object instantiate(ActiveRule activeRule, Class clazz) {
+    try {
+      Object check = clazz.newInstance();
+      configureFields(activeRule, check);
+      return check;
+
+    } catch (InstantiationException e) {
+      throw new SonarException("Can not instantiate the check related to the rule " + activeRule, e);
+
+    } catch (IllegalAccessException e) {
+      throw new SonarException("Can not instantiate the check related to the rule " + activeRule, e);
+    }
+  }
+
+  private void configureFields(ActiveRule activeRule, Object check) {
+    for (ActiveRuleParam param : activeRule.getActiveRuleParams()) {
+      Field field = getField(check, param.getKey());
+      if (field == null) {
+        throw new SonarException("The field " + param.getKey() + " does not exist or is not annotated with @RuleProperty in the class " + check.getClass().getName());
+      }
+      if (StringUtils.isNotBlank(param.getValue())) {
+        configureField(check, field, param.getValue());
+      }
+    }
+
+  }
+
+  private void configureField(Object check, Field field, String value) {
+    try {
+      field.setAccessible(true);
+
+      if (field.getType().equals(String.class)) {
+        field.set(check, value);
+
+      } else if (field.getType().getSimpleName().equals("int")) {
+        field.setInt(check, Integer.parseInt(value));
+
+      } else if (field.getType().getSimpleName().equals("short")) {
+        field.setShort(check, Short.parseShort(value));
+
+      } else if (field.getType().getSimpleName().equals("long")) {
+        field.setLong(check, Long.parseLong(value));
+
+      } else if (field.getType().getSimpleName().equals("double")) {
+        field.setDouble(check, Double.parseDouble(value));
+
+      } else if (field.getType().getSimpleName().equals("boolean")) {
+        field.setBoolean(check, Boolean.parseBoolean(value));
+
+      } else if (field.getType().getSimpleName().equals("byte")) {
+        field.setByte(check, Byte.parseByte(value));
+
+      } else if (field.getType().equals(Integer.class)) {
+        field.set(check, new Integer(Integer.parseInt(value)));
+
+      } else if (field.getType().equals(Long.class)) {
+        field.set(check, new Long(Long.parseLong(value)));
+
+      } else if (field.getType().equals(Double.class)) {
+        field.set(check, new Double(Double.parseDouble(value)));
+
+      } else if (field.getType().equals(Boolean.class)) {
+        field.set(check, Boolean.valueOf(Boolean.parseBoolean(value)));
+
+      } else {
+        throw new SonarException("The type of the field " + field + " is not supported: " + field.getType());
+      }
+    } catch (IllegalAccessException e) {
+      throw new SonarException("Can not set the value of the field " + field + " in the class: " + check.getClass().getName());
+    }
+  }
+
+  private Field getField(Object check, String key) {
+    Field[] fields = check.getClass().getDeclaredFields();
+    for (Field field : fields) {
+      RuleProperty propertyAnnotation = field.getAnnotation(RuleProperty.class);
+      if (propertyAnnotation != null) {
+        if (StringUtils.equals(key, field.getName()) || StringUtils.equals(key, propertyAnnotation.key())) {
+          return field;
+        }
+      } else {
+        CheckProperty checkAnnotation = field.getAnnotation(CheckProperty.class);
+        if (checkAnnotation != null) {
+          if (StringUtils.equals(key, field.getName()) || StringUtils.equals(key, checkAnnotation.key())) {
+            return field;
+          }
+        }
+      }
+    }
+    return null;
+  }
+
+  private String getRuleKey(Class annotatedClass) {
+    String key = null;
+    Rule ruleAnnotation = (Rule) annotatedClass.getAnnotation(Rule.class);
+    if (ruleAnnotation != null) {
+      key = ruleAnnotation.key();
+    } else {
+      Check checkAnnotation = (Check) annotatedClass.getAnnotation(Check.class);
+      if (checkAnnotation != null) {
+        key = checkAnnotation.key();
+
+      }
+    }
+    return StringUtils.defaultIfEmpty(key, annotatedClass.getCanonicalName());
+  }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/CheckFactory.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/CheckFactory.java
new file mode 100644 (file)
index 0000000..5e33866
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.api.checks;
+
+import com.google.common.collect.Maps;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.rules.ActiveRule;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * @since 2.3
+ */
+public abstract class CheckFactory<CHECK> {
+  
+  private Map<ActiveRule, CHECK> checkByActiveRule = Maps.newIdentityHashMap();
+  private Map<CHECK, ActiveRule> activeRuleByCheck = Maps.newIdentityHashMap();
+  private RulesProfile profile;
+  private String repositoryKey;
+
+  protected CheckFactory(RulesProfile profile, String repositoryKey) {
+    this.repositoryKey = repositoryKey;
+    this.profile = profile;
+  }
+
+  protected void init() {
+    checkByActiveRule.clear();
+    activeRuleByCheck.clear();
+    for (ActiveRule activeRule : profile.getActiveRulesByRepository(repositoryKey)) {
+      CHECK check = createCheck(activeRule);
+      checkByActiveRule.put(activeRule, check);
+      activeRuleByCheck.put(check, activeRule);
+    }
+  }
+
+  abstract CHECK createCheck(ActiveRule activeRule);
+
+  public final String getRepositoryKey() {
+    return repositoryKey;
+  }
+
+  public final Collection<CHECK> getChecks() {
+    return checkByActiveRule.values();
+  }
+
+  public final CHECK getCheck(ActiveRule activeRule) {
+    return checkByActiveRule.get(activeRule);
+  }
+
+  public final ActiveRule getActiveRule(CHECK check) {
+    return activeRuleByCheck.get(check);
+  }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/AnnotationRuleRepository.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/AnnotationRuleRepository.java
new file mode 100644 (file)
index 0000000..c19878d
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+
+package org.sonar.api.rules;
+
+import com.google.common.collect.Lists;
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.utils.AnnotationUtils;
+import org.sonar.check.Check;
+
+import java.lang.reflect.Field;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @since 2.3
+ */
+public final class AnnotationRuleRepository extends RuleRepository {
+
+  private static final Logger LOG = LoggerFactory.getLogger(AnnotationRuleRepository.class);
+
+  private Collection<Class> annotatedClasses;
+
+  /**
+   * Use the factory method()
+   */
+  private AnnotationRuleRepository(String key, String language, Collection<Class> annotatedClasses) {
+    super(key, language);
+    this.annotatedClasses = annotatedClasses;
+  }
+
+  public static AnnotationRuleRepository create(String key, String language, Collection<Class> annotatedClasses) {
+    return new AnnotationRuleRepository(key, language, annotatedClasses);
+  }
+
+  @Override
+  public List<Rule> createRules() {
+    List<Rule> rules = Lists.newArrayList();
+    for (Class annotatedClass : annotatedClasses) {
+      rules.add(create(annotatedClass));
+    }
+    return rules;
+  }
+
+  private Rule create(Class annotatedClass) {
+    org.sonar.check.Rule ruleAnnotation = AnnotationUtils.getClassAnnotation(annotatedClass, org.sonar.check.Rule.class);
+    if (ruleAnnotation != null) {
+      return toRule(annotatedClass, ruleAnnotation);
+    }
+    Check checkAnnotation = AnnotationUtils.getClassAnnotation(annotatedClass, Check.class);
+    if (checkAnnotation != null) {
+      return toRule(annotatedClass, checkAnnotation);
+    }
+    LOG.warn("The class " + annotatedClass.getCanonicalName() + " is not a check template. It should be annotated with " + Rule.class);
+    return null;
+  }
+
+  private Rule toRule(Class clazz, org.sonar.check.Rule ruleAnnotation) {
+    String key = StringUtils.defaultIfEmpty(ruleAnnotation.key(), clazz.getCanonicalName());
+    Rule rule = Rule.create(getKey(), key, ruleAnnotation.name());
+    rule.setDescription(ruleAnnotation.description());
+    rule.setRulesCategory(RulesCategory.fromIsoCategory(ruleAnnotation.isoCategory()));
+    rule.setPriority(RulePriority.fromCheckPriority(ruleAnnotation.priority()));
+
+    Field[] fields = clazz.getDeclaredFields();
+    if (fields != null) {
+      for (Field field : fields) {
+        addRuleProperty(rule, field);
+      }
+    }
+
+    return rule;
+  }
+
+  private Rule toRule(Class clazz, Check checkAnnotation) {
+    String key = StringUtils.defaultIfEmpty(checkAnnotation.key(), clazz.getCanonicalName());
+    Rule rule = Rule.create(getKey(), key, checkAnnotation.title());
+    rule.setDescription(checkAnnotation.description());
+    rule.setRulesCategory(RulesCategory.fromIsoCategory(checkAnnotation.isoCategory()));
+    rule.setPriority(RulePriority.fromCheckPriority(checkAnnotation.priority()));
+
+    Field[] fields = clazz.getDeclaredFields();
+    if (fields != null) {
+      for (Field field : fields) {
+        addCheckProperty(rule, field);
+      }
+    }
+    return rule;
+  }
+
+  private void addRuleProperty(Rule rule, Field field) {
+    org.sonar.check.RuleProperty propertyAnnotation = field.getAnnotation(org.sonar.check.RuleProperty.class);
+    if (propertyAnnotation != null) {
+      String fieldKey = StringUtils.defaultIfEmpty(propertyAnnotation.key(), field.getName());
+      RuleParam param = rule.createParameter(fieldKey);
+      param.setDescription(propertyAnnotation.description());
+      param.setDefaultValue(propertyAnnotation.defaultValue());
+    }
+  }
+
+  private void addCheckProperty(Rule rule, Field field) {
+    org.sonar.check.CheckProperty propertyAnnotation = field.getAnnotation(org.sonar.check.CheckProperty.class);
+    if (propertyAnnotation != null) {
+      String fieldKey = StringUtils.defaultIfEmpty(propertyAnnotation.key(), field.getName());
+      RuleParam param = rule.createParameter(fieldKey);
+      param.setDescription(propertyAnnotation.description());
+    }
+  }
+}
index 08a5343f842e476d4865ab2227cbd582f5ad20a3..190c739fd03d80e4a800dee4dcdd504d3fe6e49a 100644 (file)
@@ -151,4 +151,23 @@ public class RulesCategory extends BaseIdentifiable {
     }\r
     return null;\r
   }\r
+\r
+  public static RulesCategory fromIsoCategory(IsoCategory iso) {\r
+    if (iso==IsoCategory.Efficiency) {\r
+      return Iso9126RulesCategories.EFFICIENCY;\r
+    }\r
+    if (iso==IsoCategory.Maintainability) {\r
+      return Iso9126RulesCategories.MAINTAINABILITY;\r
+    }\r
+    if (iso==IsoCategory.Portability) {\r
+      return Iso9126RulesCategories.PORTABILITY;\r
+    }\r
+    if (iso==IsoCategory.Reliability) {\r
+      return Iso9126RulesCategories.RELIABILITY;\r
+    }\r
+    if (iso==IsoCategory.Usability) {\r
+      return Iso9126RulesCategories.USABILITY;\r
+    }\r
+    return null;\r
+  }\r
 }\r
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/checks/AnnotationCheckFactoryTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/checks/AnnotationCheckFactoryTest.java
new file mode 100644 (file)
index 0000000..7eec4aa
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.api.checks;
+
+import org.junit.Test;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.rules.ActiveRule;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.utils.SonarException;
+
+import java.util.Arrays;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+
+public class AnnotationCheckFactoryTest {
+
+  @Test
+  public void createCheckWithoutProperties() {
+    RulesProfile profile = RulesProfile.create("repo", "java");
+    ActiveRule activeRule = profile.activateRule(Rule.create("repo", "org.sonar.api.checks.CheckWithoutProperties", ""), null);
+    AnnotationCheckFactory factory = AnnotationCheckFactory.create(profile, "repo", Arrays.<Class>asList(CheckWithoutProperties.class));
+
+    Object check = factory.getCheck(activeRule);
+    assertNotNull(check);
+    assertThat(check, is(CheckWithoutProperties.class));
+  }
+
+  @Test
+  public void createCheckWithStringProperty() {
+    RulesProfile profile = RulesProfile.create("repo", "java");
+    Rule rule = Rule.create("repo", "org.sonar.api.checks.CheckWithStringProperty", "");
+    rule.createParameter("pattern");
+
+    ActiveRule activeRule = profile.activateRule(rule, null);
+    activeRule.setParameter("pattern", "foo");
+    AnnotationCheckFactory factory = AnnotationCheckFactory.create(profile, "repo", Arrays.<Class>asList(CheckWithStringProperty.class));
+
+    Object check = factory.getCheck(activeRule);
+    assertNotNull(check);
+    assertThat(check, is(CheckWithStringProperty.class));
+    assertThat(((CheckWithStringProperty) check).getPattern(), is("foo"));
+  }
+
+  @Test(expected = SonarException.class)
+  public void failIfMissingProperty() {
+    RulesProfile profile = RulesProfile.create("repo", "java");
+    Rule rule = Rule.create("repo", "org.sonar.api.checks.CheckWithStringProperty", "");
+    rule.createParameter("unknown");
+
+    ActiveRule activeRule = profile.activateRule(rule, null);
+    activeRule.setParameter("unknown", "bar");
+    AnnotationCheckFactory.create(profile, "repo", Arrays.<Class>asList(CheckWithStringProperty.class));
+  }
+
+  @Test
+  public void createCheckWithPrimitiveProperties() {
+    RulesProfile profile = RulesProfile.create("repo", "java");
+    Rule rule = Rule.create("repo", "org.sonar.api.checks.CheckWithPrimitiveProperties", "");
+    rule.createParameter("max");
+    rule.createParameter("ignore");
+
+    ActiveRule activeRule = profile.activateRule(rule, null);
+    activeRule.setParameter("max", "300");
+    activeRule.setParameter("ignore", "true");
+    AnnotationCheckFactory factory = AnnotationCheckFactory.create(profile, "repo", Arrays.<Class>asList(CheckWithPrimitiveProperties.class));
+
+    Object check = factory.getCheck(activeRule);
+    assertThat(((CheckWithPrimitiveProperties) check).getMax(), is(300));
+    assertThat(((CheckWithPrimitiveProperties) check).isIgnore(), is(true));
+  }
+
+  @Test
+  public void createCheckWithIntegerProperty() {
+    RulesProfile profile = RulesProfile.create("repo", "java");
+    Rule rule = Rule.create("repo", "org.sonar.api.checks.CheckWithIntegerProperty", "");
+    rule.createParameter("max");
+
+    ActiveRule activeRule = profile.activateRule(rule, null);
+    activeRule.setParameter("max", "300");
+    AnnotationCheckFactory factory = AnnotationCheckFactory.create(profile, "repo", Arrays.<Class>asList(CheckWithIntegerProperty.class));
+
+    Object check = factory.getCheck(activeRule);
+    assertThat(((CheckWithIntegerProperty) check).getMax(), is(300));
+  }
+
+
+  @Test(expected = SonarException.class)
+  public void failIfPropertyTypeIsNotSupported() {
+    RulesProfile profile = RulesProfile.create("repo", "java");
+    Rule rule = Rule.create("repo", "org.sonar.api.checks.CheckWithUnsupportedPropertyType", "");
+    rule.createParameter("max");
+
+    ActiveRule activeRule = profile.activateRule(rule, null);
+    activeRule.setParameter("max", "300");
+    AnnotationCheckFactory.create(profile, "repo", Arrays.<Class>asList(CheckWithUnsupportedPropertyType.class));
+  }
+
+  @Test
+  public void shouldOverridePropertyKey() {
+    RulesProfile profile = RulesProfile.create("repo", "java");
+    Rule rule = Rule.create("repo", "org.sonar.api.checks.CheckWithOverriddenPropertyKey", "");
+    rule.createParameter("maximum");
+
+    ActiveRule activeRule = profile.activateRule(rule, null);
+    activeRule.setParameter("maximum", "300");
+    AnnotationCheckFactory factory = AnnotationCheckFactory.create(profile, "repo", Arrays.<Class>asList(CheckWithOverriddenPropertyKey.class));
+
+    Object check = factory.getCheck(activeRule);
+    assertThat(((CheckWithOverriddenPropertyKey) check).getMax(), is(300));
+  }
+}
+
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithIntegerProperty.java b/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithIntegerProperty.java
new file mode 100644 (file)
index 0000000..3232abd
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.api.checks;
+
+import org.sonar.check.*;
+
+@Rule(isoCategory = IsoCategory.Efficiency, priority = Priority.CRITICAL)
+class CheckWithIntegerProperty {
+
+  @RuleProperty
+  private Integer max;
+
+  public Integer getMax() {
+    return max;
+  }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithOverriddenPropertyKey.java b/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithOverriddenPropertyKey.java
new file mode 100644 (file)
index 0000000..4361a06
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.api.checks;
+
+import org.sonar.check.IsoCategory;
+import org.sonar.check.Priority;
+import org.sonar.check.Rule;
+import org.sonar.check.RuleProperty;
+
+@Rule(isoCategory = IsoCategory.Efficiency, priority = Priority.CRITICAL)
+class CheckWithOverriddenPropertyKey{
+
+  @RuleProperty(key = "maximum")
+  private int max = 50;
+
+  public int getMax() {
+    return max;
+  }
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithPrimitiveProperties.java b/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithPrimitiveProperties.java
new file mode 100644 (file)
index 0000000..7777281
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.api.checks;
+
+import org.sonar.check.*;
+
+@Rule(isoCategory = IsoCategory.Efficiency, priority = Priority.CRITICAL)
+class CheckWithPrimitiveProperties {
+
+  @RuleProperty(description = "Maximum threshold")
+  private int max = 50;
+
+  @RuleProperty
+  private boolean ignore;
+
+  public int getMax() {
+    return max;
+  }
+
+  public boolean isIgnore() {
+    return ignore;
+  }
+}
+
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithStringProperty.java b/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithStringProperty.java
new file mode 100644 (file)
index 0000000..a0ecb64
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.api.checks;
+
+import org.sonar.check.*;
+
+@Rule(isoCategory = IsoCategory.Efficiency, priority = Priority.CRITICAL)
+class CheckWithStringProperty {
+
+  @RuleProperty
+  private String pattern;
+
+  public String getPattern() {
+    return pattern;
+  }
+}
+
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithUnsupportedPropertyType.java b/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithUnsupportedPropertyType.java
new file mode 100644 (file)
index 0000000..9e55a17
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+ */
+package org.sonar.api.checks;
+
+import org.sonar.check.*;
+
+@Rule(isoCategory = IsoCategory.Efficiency, priority = Priority.CRITICAL)
+class CheckWithUnsupportedPropertyType {
+
+  @RuleProperty
+  private StringBuilder max = null;
+
+}
diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithoutProperties.java b/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithoutProperties.java
new file mode 100644 (file)
index 0000000..ad83842
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+* Sonar, open source software quality management tool.
+* Copyright (C) 2009 SonarSource SA
+* mailto:contact AT sonarsource DOT com
+*
+* Sonar is free software; you can redistribute it and/or
+* modify it under the terms of the GNU Lesser General Public
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or (at your option) any later version.
+*
+* Sonar is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+* Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public
+* License along with Sonar; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
+*/
+package org.sonar.api.checks;
+
+import org.sonar.check.IsoCategory;
+import org.sonar.check.Priority;
+import org.sonar.check.Rule;
+
+@Rule(isoCategory = IsoCategory.Efficiency, priority = Priority.CRITICAL)
+class CheckWithoutProperties {
+
+}
+