From: simonbrandhof Date: Thu, 23 Sep 2010 13:29:43 +0000 (+0000) Subject: add the package org.sonar.api.checks in order to implement its own rules engine X-Git-Tag: 2.6~970 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=f908b8b900e51728bbf04291e5d241e0317a18d0;p=sonarqube.git add the package org.sonar.api.checks in order to implement its own rules engine --- diff --git a/sonar-check-api/src/main/java/org/sonar/check/AnnotationIntrospector.java b/sonar-check-api/src/main/java/org/sonar/check/AnnotationIntrospector.java index 15def7431c3..d368a616240 100644 --- a/sonar-check-api/src/main/java/org/sonar/check/AnnotationIntrospector.java +++ b/sonar-check-api/src/main/java/org/sonar/check/AnnotationIntrospector.java @@ -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() { diff --git a/sonar-check-api/src/main/java/org/sonar/check/BelongsToProfiles.java b/sonar-check-api/src/main/java/org/sonar/check/BelongsToProfiles.java index a7d15fdf891..16ada04cce8 100644 --- a/sonar-check-api/src/main/java/org/sonar/check/BelongsToProfiles.java +++ b/sonar-check-api/src/main/java/org/sonar/check/BelongsToProfiles.java @@ -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 {}; diff --git a/sonar-check-api/src/main/java/org/sonar/check/Check.java b/sonar-check-api/src/main/java/org/sonar/check/Check.java index 8a8ae8ef1af..6731d041560 100644 --- a/sonar-check-api/src/main/java/org/sonar/check/Check.java +++ b/sonar-check-api/src/main/java/org/sonar/check/Check.java @@ -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 { /** diff --git a/sonar-check-api/src/main/java/org/sonar/check/CheckProperty.java b/sonar-check-api/src/main/java/org/sonar/check/CheckProperty.java index 17ac5801e53..935e8e8592d 100644 --- a/sonar-check-api/src/main/java/org/sonar/check/CheckProperty.java +++ b/sonar-check-api/src/main/java/org/sonar/check/CheckProperty.java @@ -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 { /** diff --git a/sonar-check-api/src/main/java/org/sonar/check/Message.java b/sonar-check-api/src/main/java/org/sonar/check/Message.java index 0eecb1437c2..94bea04c303 100644 --- a/sonar-check-api/src/main/java/org/sonar/check/Message.java +++ b/sonar-check-api/src/main/java/org/sonar/check/Message.java @@ -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 index 00000000000..2b072ccbfa0 --- /dev/null +++ b/sonar-check-api/src/main/java/org/sonar/check/Rule.java @@ -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 index 00000000000..fc545a24163 --- /dev/null +++ b/sonar-check-api/src/main/java/org/sonar/check/RuleProperty.java @@ -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 ""; +} diff --git a/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/AnnotationCheckerFactoryTest.java b/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/AnnotationCheckerFactoryTest.java index 8235725ac56..8e89d46dd43 100644 --- a/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/AnnotationCheckerFactoryTest.java +++ b/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/AnnotationCheckerFactoryTest.java @@ -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; diff --git a/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckWithUnsupportedPropertyType.java b/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckWithUnsupportedPropertyType.java index b5b9f1bacbb..3fb27011290 100644 --- a/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckWithUnsupportedPropertyType.java +++ b/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckWithUnsupportedPropertyType.java @@ -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 { diff --git a/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckerWithIntegerProperty.java b/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckerWithIntegerProperty.java index 4ae71746cf3..1c0f3983a60 100644 --- a/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckerWithIntegerProperty.java +++ b/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckerWithIntegerProperty.java @@ -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; diff --git a/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckerWithPrimitiveProperties.java b/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckerWithPrimitiveProperties.java index 15af2ade92f..3bf938f3d9e 100644 --- a/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckerWithPrimitiveProperties.java +++ b/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckerWithPrimitiveProperties.java @@ -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 { diff --git a/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckerWithStringProperty.java b/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckerWithStringProperty.java index a7f62a248e3..db40a9f1bc8 100644 --- a/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckerWithStringProperty.java +++ b/sonar-deprecated/src/test/java/org/sonar/api/checks/checkers/CheckerWithStringProperty.java @@ -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 index 00000000000..918a755e16a --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/AnnotationCheckFactory.java @@ -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 checkClassesByKey = Maps.newHashMap(); + + private AnnotationCheckFactory(RulesProfile profile, String repositoryKey, Collection checkClasses) { + super(profile, repositoryKey); + groupClassesByKey(checkClasses); + } + + + public static AnnotationCheckFactory create(RulesProfile profile, String repositoryKey, Collection checkClasses) { + AnnotationCheckFactory factory = new AnnotationCheckFactory(profile, repositoryKey, checkClasses); + factory.init(); + return factory; + } + + private void groupClassesByKey(Collection 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 index 00000000000..5e338665c6e --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/CheckFactory.java @@ -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 { + + private Map checkByActiveRule = Maps.newIdentityHashMap(); + private Map 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 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 index 00000000000..c19878dd800 --- /dev/null +++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/AnnotationRuleRepository.java @@ -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 annotatedClasses; + + /** + * Use the factory method() + */ + private AnnotationRuleRepository(String key, String language, Collection annotatedClasses) { + super(key, language); + this.annotatedClasses = annotatedClasses; + } + + public static AnnotationRuleRepository create(String key, String language, Collection annotatedClasses) { + return new AnnotationRuleRepository(key, language, annotatedClasses); + } + + @Override + public List createRules() { + List 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()); + } + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesCategory.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesCategory.java index 08a5343f842..190c739fd03 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesCategory.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesCategory.java @@ -151,4 +151,23 @@ public class RulesCategory extends BaseIdentifiable { } return null; } + + public static RulesCategory fromIsoCategory(IsoCategory iso) { + if (iso==IsoCategory.Efficiency) { + return Iso9126RulesCategories.EFFICIENCY; + } + if (iso==IsoCategory.Maintainability) { + return Iso9126RulesCategories.MAINTAINABILITY; + } + if (iso==IsoCategory.Portability) { + return Iso9126RulesCategories.PORTABILITY; + } + if (iso==IsoCategory.Reliability) { + return Iso9126RulesCategories.RELIABILITY; + } + if (iso==IsoCategory.Usability) { + return Iso9126RulesCategories.USABILITY; + } + return null; + } } 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 index 00000000000..7eec4aaac6a --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/checks/AnnotationCheckFactoryTest.java @@ -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.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.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.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.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.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.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.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 index 00000000000..3232abdf7b3 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithIntegerProperty.java @@ -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 index 00000000000..4361a069a85 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithOverriddenPropertyKey.java @@ -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 index 00000000000..77772819be0 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithPrimitiveProperties.java @@ -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 index 00000000000..a0ecb648003 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithStringProperty.java @@ -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 index 00000000000..9e55a1733ff --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithUnsupportedPropertyType.java @@ -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 index 00000000000..ad83842a1b2 --- /dev/null +++ b/sonar-plugin-api/src/test/java/org/sonar/api/checks/CheckWithoutProperties.java @@ -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 { + +} +