aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-plugin-api/src/main/java/org/sonar
diff options
context:
space:
mode:
Diffstat (limited to 'sonar-plugin-api/src/main/java/org/sonar')
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/BatchComponent.java29
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/BatchExtension.java29
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java113
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/Extension.java28
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/Plugin.java55
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/Plugins.java85
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/Properties.java33
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/Property.java74
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/ServerComponent.java29
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/ServerExtension.java28
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractCoverageExtension.java70
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractCpdMapping.java40
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractDirectoriesDecorator.java55
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractDivisionDecorator.java92
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractFileComplexityDecorator.java88
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractFilesDecorator.java55
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractFunctionComplexityDecorator.java87
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractFunctionComplexityDistributionDecorator.java83
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractSourceImporter.java114
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractSumChildrenDecorator.java89
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractViolationsStaxParser.java192
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java227
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/BuildBreaker.java36
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/CheckProject.java31
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/CpdMapping.java41
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/Decorator.java32
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/DecoratorContext.java144
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/DefaultFormulaContext.java58
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/DefaultFormulaData.java65
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/DependedUpon.java42
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/DependsUpon.java41
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/Event.java169
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/FormulaDecorator.java119
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/GeneratesViolations.java26
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/Phase.java39
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/PostJob.java38
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/ProjectClasspath.java97
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/Purge.java40
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/PurgeContext.java31
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/ResourceFilter.java39
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/Sensor.java63
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java175
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java84
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/SquidSearch.java49
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/SquidUtils.java52
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/TimeMachine.java49
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/TimeMachineQuery.java210
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/DependsUponCustomRules.java28
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/DependsUponMavenPlugin.java34
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/MavenPlugin.java397
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/MavenPluginHandler.java72
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/MavenSurefireUtils.java49
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/MavenUtils.java140
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/charts/AbstractChart.java97
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/charts/Chart.java41
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/charts/ChartParameters.java184
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/NoSonarFilter.java50
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/checkers/AnnotationCheckerFactory.java157
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/checkers/CheckerFactory.java31
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/checkers/MessageDispatcher.java106
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/checkers/UnvalidCheckerException.java37
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/AnnotationCheckProfileFactory.java67
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/Check.java109
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/CheckProfile.java131
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/CheckProfileProvider.java33
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/CheckProfileXmlMarshaller.java154
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/AnnotationCheckTemplateFactory.java112
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/BundleCheckTemplate.java110
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/BundleCheckTemplateProperty.java64
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplate.java134
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplateFactory.java32
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplateProperty.java71
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplateRepositories.java62
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplateRepository.java203
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/DefaultCheckTemplate.java81
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/DefaultCheckTemplateProperty.java62
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/XmlCheckTemplateFactory.java100
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/database/BaseIdentifiable.java42
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/database/DatabaseProperties.java55
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/database/DatabaseSession.java78
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/database/model/AsyncMeasureSnapshot.java172
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureData.java88
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureModel.java659
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/database/model/ResourceModel.java323
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/database/model/RuleFailureModel.java135
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/database/model/Snapshot.java292
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/database/model/SnapshotSource.java82
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/database/model/User.java87
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/design/Dependency.java127
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/design/DependencyDto.java207
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/AverageComplexityFormula.java80
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java445
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/CountDistributionBuilder.java164
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/Formula.java33
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/FormulaContext.java36
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/FormulaData.java38
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/MeanAggregationFormula.java63
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java554
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasureBuilder.java29
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasureUtils.java135
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilter.java31
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java252
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java534
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/Metrics.java32
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/PersistenceMode.java42
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/PropertiesBuilder.java83
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/RangeDistributionBuilder.java249
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java132
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/SumChildDistributionFormula.java50
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/SumChildValuesFormula.java47
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/measures/WeightedMeanAggregationFormula.java68
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/platform/Environment.java50
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/platform/PluginRepository.java113
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/platform/Server.java37
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/platform/ServerFileSystem.java39
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/platform/ServerStartHandler.java31
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/platform/ServerStopHandler.java31
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/profiles/Alert.java211
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/profiles/AnnotationProfileImporter.java69
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfileDefinition.java65
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfileExporter.java46
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfileImporter.java46
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfilePrototype.java149
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/profiles/RulesProfile.java350
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileDefinition.java66
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileExporter.java111
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileImporter.java125
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/qualitymodel/Characteristic.java255
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/qualitymodel/Model.java168
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/qualitymodel/ModelDefinition.java61
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/qualitymodel/ModelProvider.java42
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/AbstractLanguage.java93
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/DefaultProjectFileSystem.java350
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/Directory.java97
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/File.java207
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/Java.java60
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/JavaFile.java233
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/JavaPackage.java122
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/Language.java47
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/Languages.java77
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/Library.java121
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/Project.java412
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/ProjectFileSystem.java104
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/ProjectLink.java134
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/ProjectUtils.java43
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/Resource.java197
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceUtils.java130
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/AbstractImportableRulesRepository.java75
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/AbstractRulesRepository.java76
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRule.java228
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRuleParam.java145
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/ConfigurationExportable.java28
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/ConfigurationImportable.java28
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/Iso9126RulesCategories.java37
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java410
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleAnnotationUtils.java95
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleParam.java162
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/RulePriority.java94
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/RulePriorityMapper.java28
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleProvider.java36
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleQuery.java64
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleRepository.java84
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleUtils.java55
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesCategory.java154
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesManager.java114
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesRepository.java60
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/StandardProfileXmlParser.java118
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/StandardRuleXmlFormat.java192
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/StandardRulesXmlParser.java160
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/Violation.java153
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/ViolationFilter.java36
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/xml/Profile.java64
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/xml/Property.java46
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/rules/xml/Rule.java81
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/security/GroupRole.java84
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/security/LoginPasswordAuthenticator.java36
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/security/UserRole.java81
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/AnnotationUtils.java47
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/HttpDownloader.java110
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/IocContainer.java54
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/KeyValue.java72
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/KeyValueFormat.java241
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/LocalizedMessages.java123
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/Logs.java39
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/ManifestUtils.java60
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/ParsingUtils.java74
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/ServerHttpClient.java162
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/SonarException.java41
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/StaxParser.java204
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/TempFileUtils.java43
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/TimeProfiler.java75
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/ValidationMessages.java85
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/WildcardPattern.java107
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/XmlParserException.java40
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/XpathParser.java268
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/ZipUtils.java165
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/dag/CyclicDependenciesException.java43
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/dag/DirectAcyclicGraph.java93
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/utils/dag/Node.java126
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/AbstractDashboardWidget.java36
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/AbstractRubyTemplate.java100
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/CodeColorizerFormat.java73
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/DefaultTab.java37
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/Footer.java33
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/GwtExtension.java29
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/GwtPage.java31
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/NavigationSection.java45
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/Page.java27
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/ResourceLanguage.java42
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/ResourceQualifier.java42
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/ResourceScope.java42
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/RubyRailsPage.java32
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/RubyRailsWebservice.java41
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/RubyRailsWidget.java40
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/Section.java29
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/UserRole.java45
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/View.java32
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/Webservice.java36
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/Widget.java26
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/AbstractPage.java50
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/AbstractViewer.java216
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/ResourceDictionary.java59
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/Utils.java154
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/AbstractResourceQuery.java40
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/BaseQueryCallback.java52
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/FileSource.java37
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/JSONHandlerDispatcher.java48
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/JsonUtils.java182
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Measure.java152
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/MetricsQuery.java104
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Properties.java44
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/PropertiesQuery.java75
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Property.java52
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Query.java25
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/QueryCallBack.java32
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Resource.java187
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Resources.java63
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/ResourcesQuery.java256
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/ResponsePOJO.java27
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Rule.java57
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/SequentialQueries.java112
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/SourcesQuery.java101
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Violation.java80
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Violations.java87
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/ViolationsQuery.java162
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/VoidResponse.java24
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/WSMetrics.java243
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/widgets/LoadingLabel.java34
248 files changed, 25218 insertions, 0 deletions
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/BatchComponent.java b/sonar-plugin-api/src/main/java/org/sonar/api/BatchComponent.java
new file mode 100644
index 00000000000..d42f4c5f87a
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/BatchComponent.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * Dependency Injection : all the classes implementing this interface are available in the batch IoC container.
+ * Just add a parameter to the constructor of your component.
+ *
+ * @since 2.2
+ */
+public interface BatchComponent {
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/BatchExtension.java b/sonar-plugin-api/src/main/java/org/sonar/api/BatchExtension.java
new file mode 100644
index 00000000000..45d4d79909a
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/BatchExtension.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * Batch extension point.
+ *
+ * @since 1.10
+ */
+public interface BatchExtension extends Extension, BatchComponent {
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
new file mode 100644
index 00000000000..542ef3872db
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java
@@ -0,0 +1,113 @@
+/*
+ * 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;
+
+/**
+ * CoreProperties is used to group various properties of Sonar as well
+ * as default values of configuration in a single place
+ *
+ * @since 1.11
+ */
+public interface CoreProperties {
+
+ /* Global settings */
+ String SONAR_HOME = "sonar.home";
+ String PROJECT_BRANCH_PROPERTY = "sonar.branch";
+ String PROJECT_VERSION_PROPERTY = "sonar.projectVersion";
+
+ /**
+ * Value format is yyyy-MM-dd
+ */
+ String PROJECT_DATE_PROPERTY = "sonar.projectDate";
+ String PROJECT_LANGUAGE_PROPERTY = "sonar.language";
+ String DYNAMIC_ANALYSIS_PROPERTY = "sonar.dynamicAnalysis";
+ String PROJECT_EXCLUSIONS_PROPERTY = "sonar.exclusions";
+ String REUSE_RULES_CONFIGURATION_PROPERTY = "sonar.reuseExistingRulesConfiguration";
+
+
+ /* Checkstyle */
+ String CHECKSTYLE_PLUGIN = "checkstyle";
+
+ /* Cobertura */
+ String COBERTURA_PLUGIN = "cobertura";
+ String COBERTURA_REPORT_PATH_PROPERTY = "sonar.cobertura.reportPath";
+ String COBERTURA_MAXMEM_PROPERTY = "sonar.cobertura.maxmen";
+ String COBERTURA_MAXMEM_DEFAULT_VALUE = "64m";
+
+ /* Sonar Core */
+ String CORE_PLUGIN = "core";
+ String CORE_COVERAGE_PLUGIN_PROPERTY = "sonar.core.codeCoveragePlugin";
+ String CORE_IMPORT_SOURCES_PROPERTY = "sonar.importSources";
+ boolean CORE_IMPORT_SOURCES_DEFAULT_VALUE = true;
+ String CORE_SKIPPED_MODULES_PROPERTY = "sonar.skippedModules";
+ String CORE_RULE_WEIGHTS_PROPERTY = "sonar.core.rule.weight";
+ String CORE_RULE_WEIGHTS_DEFAULT_VALUE = "INFO=0;MINOR=1;MAJOR=3;CRITICAL=5;BLOCKER=10";
+ String CORE_TENDENCY_DEPTH_PROPERTY = "tendency.depth";
+ int CORE_TENDENCY_DEPTH_DEFAULT_VALUE = 30;
+ String CORE_FORCE_AUTHENTICATION_PROPERTY = "sonar.forceAuthentication";
+ boolean CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE = false;
+ String CORE_ALLOW_USERS_TO_SIGNUP_PROPERTY = "sonar.allowUsersToSignUp";
+ String CORE_DEFAULT_GROUP = "sonar.defaultGroup";
+ String CORE_DEFAULT_GROUP_DEFAULT_VALUE = "sonar-users";
+ boolean CORE_ALLOW_USERS_TO_SIGNUP_DEAULT_VALUE = false;
+ String CORE_AUTHENTICATOR_CLASS = "sonar.authenticator.class";
+ String CORE_AUTHENTICATOR_IGNORE_STARTUP_FAILURE = "sonar.authenticator.ignoreStartupFailure";
+ String CORE_AUTHENTICATOR_CREATE_USERS = "sonar.authenticator.createUsers";
+ String SERVER_VERSION = "sonar.core.version";
+ String SERVER_ID = "sonar.core.id";
+ String SERVER_STARTTIME = "sonar.core.startTime"; // format is yyyy-MM-dd'T'HH:mm:ssZ
+
+ /* CPD */
+ String CPD_PLUGIN = "cpd";
+ String CPD_MINIMUM_TOKENS_PROPERTY = "sonar.cpd.minimumTokens";
+ int CPD_MINIMUM_TOKENS_DEFAULT_VALUE = 100;
+ String CPD_IGNORE_LITERALS_PROPERTY = "sonar.cpd.ignore_literals";
+ String CPD_IGNORE_LITERALS_DEFAULT_VALUE = "true";
+ String CPD_IGNORE_IDENTIFIERS_PROPERTY = "sonar.cpd.ignore_identifiers";
+ String CPD_IGNORE_IDENTIFIERS_DEFAULT_VALUE = "false";
+ String CPD_SKIP_PROPERTY = "sonar.cpd.skip";
+
+ /* Design */
+ String DESIGN_SKIP_DESIGN_PROPERTY = "sonar.skipDesign";
+ boolean DESIGN_SKIP_DESIGN_DEFAULT_VALUE = false;
+
+ /* Findbugs */
+ String FINDBUGS_PLUGIN = "findbugs";
+ String FINDBUGS_EFFORT_PROPERTY = "sonar.findbugs.effort";
+ String FINDBUGS_EFFORT_DEFAULT_VALUE = "Default";
+ String FINDBUGS_REPORT_PATH = "sonar.findbugs.reportPath";
+ String FINDBUGS_MAXHEAP_PROPERTY = "sonar.findbugs.maxHeap";
+ String FINDBUGS_TIMEOUT_PROPERTY = "sonar.findbugs.timeout";
+ int FINDBUGS_MAXHEAP_DEFAULT_VALUE = 512;
+
+ /* Google Analytics */
+ String GOOGLE_ANALYTICS_PLUGIN = "google-analytics";
+ String GOOGLE_ANALYTICS_ACCOUNT_PROPERTY = "sonar.google-analytics.account";
+
+ /* PMD */
+ String PMD_PLUGIN = "pmd";
+
+ /* Squid */
+ String SQUID_PLUGIN = "squid";
+
+ /* Surefire */
+ String SUREFIRE_PLUGIN = "surefire";
+ String SUREFIRE_REPORTS_PATH_PROPERTY = "sonar.surefire.reportsPath";
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/Extension.java b/sonar-plugin-api/src/main/java/org/sonar/api/Extension.java
new file mode 100644
index 00000000000..5bcd1700101
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/Extension.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+/**
+ * Extension point.
+ *
+ * @since 1.10
+ */
+public interface Extension {
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/Plugin.java b/sonar-plugin-api/src/main/java/org/sonar/api/Plugin.java
new file mode 100644
index 00000000000..a0a953fbde9
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/Plugin.java
@@ -0,0 +1,55 @@
+/*
+ * 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;
+
+import java.util.List;
+
+/**
+ * A plugin is a group of extensions. See <code>org.sonar.api.Extension</code> interface to get all extension points.
+ * <p/>
+ * <p>The manifest property <code>Plugin-Class</code> must declare the name of the implementation class.
+ * See META-INF/MANIFEST.MF.</p>
+ *
+ * @see org.sonar.api.Extension
+ * @since 1.10
+ */
+public interface Plugin {
+
+ /**
+ * Unique key within sonar plugins
+ */
+ String getKey();
+
+ /**
+ * Descriptive name
+ */
+ String getName();
+
+ /**
+ * description of the plugin, can contains html or ruby code
+ */
+ String getDescription();
+
+ /**
+ * Classes of the implemented extensions.
+ */
+ List getExtensions();
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/Plugins.java b/sonar-plugin-api/src/main/java/org/sonar/api/Plugins.java
new file mode 100644
index 00000000000..eca185e4920
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/Plugins.java
@@ -0,0 +1,85 @@
+/*
+ * 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;
+
+import org.sonar.api.platform.PluginRepository;
+
+import java.util.Collection;
+
+/**
+ * Plugins dictionnary. This class is for internal use
+ *
+ * @since 1.10
+ * @deprecated
+ */
+public class Plugins {
+
+ private PluginRepository pluginProvider;
+
+ /**
+ * Creates the dictionnary of plugins
+ */
+ public Plugins(PluginRepository pluginProvider) {
+ this.pluginProvider = pluginProvider;
+ }
+
+ /**
+ * Gives a collection of available plugins in the Sonar instance
+ */
+ public Collection<Plugin> getPlugins() {
+ return pluginProvider.getPlugins();
+ }
+
+ /**
+ * Returns a plugin based on its key
+ */
+ public Plugin getPlugin(String key) {
+ return pluginProvider.getPlugin(key);
+ }
+
+
+ /**
+ * Returns a plugin based on its extension
+ */
+ public Plugin getPluginByExtension(Class<? extends Extension> clazz) {
+ return pluginProvider.getPluginForExtension(clazz);
+ }
+
+ /**
+ * Returns a plugin key based on its extension
+ */
+ public String getPluginKeyByExtension(Class<? extends Extension> clazz) {
+ return pluginProvider.getPluginKeyForExtension(clazz);
+ }
+
+ /**
+ * Returns a plugin based on its extension
+ */
+ public Plugin getPluginByExtension(Extension extension) {
+ return pluginProvider.getPluginForExtension(extension);
+ }
+
+ /**
+ * Returns the list of properties of a plugin
+ */
+ public Property[] getProperties(Plugin plugin) {
+ return pluginProvider.getProperties(plugin);
+ }
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/Properties.java b/sonar-plugin-api/src/main/java/org/sonar/api/Properties.java
new file mode 100644
index 00000000000..eef5ee38a4e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/Properties.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;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Plugin properties. This annotation is only used on classes implementing org.sonar.api.Plugin.
+ *
+ * @since 1.10
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Properties {
+ Property[] value();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/Property.java b/sonar-plugin-api/src/main/java/org/sonar/api/Property.java
new file mode 100644
index 00000000000..fe034f36a40
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/Property.java
@@ -0,0 +1,74 @@
+/*
+ * 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Property value can be set in different ways :
+ * <ul>
+ * <li>System property</li>
+ * <li>Maven command-line (-Dfoo=bar)</li>
+ * <li>Maven pom.xml (element <properties>)</li>
+ * <li>Maven settings.xml</li>
+ * <li>Sonar web interface</li>
+ * </ul>
+ * <p/>
+ * Value is accessible in batch extensions via the Configuration object of class <code>org.sonar.api.resources.Project</code>
+ * (see method <code>getConfiguration()</code>).
+ * <p/>
+ * <p><strong>Must be used in <code>org.sonar.api.Plugin</code> classes only.</strong></p>
+ *
+ * @since 1.10
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface Property {
+
+ /**
+ * Unique key within all plugins. It's recommended to prefix the key by 'sonar.' and the plugin name. Examples :
+ * 'sonar.cobertura.reportPath' and 'sonar.cpd.minimumTokens'.
+ */
+ String key();
+
+ String defaultValue() default "";
+
+ String name();
+
+ String description() default "";
+
+ /**
+ * Is the property displayed in projet settings page ?
+ */
+ boolean project() default false;
+
+ /**
+ * Is the property displayed in module settings page ? A module is a maven sub-project.
+ */
+ boolean module() default false;
+
+ /**
+ * Is the property displayed in global settings page ?
+ */
+ boolean global() default true;
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/ServerComponent.java b/sonar-plugin-api/src/main/java/org/sonar/api/ServerComponent.java
new file mode 100644
index 00000000000..6d7a2ed5699
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/ServerComponent.java
@@ -0,0 +1,29 @@
+/*
+ * 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;
+
+/**
+ * Dependency Injection : all the classes implementing this interface are available in the server IoC container.
+ * Just add a parameter to the constructor of your component.
+ *
+ * @since 2.2
+ */
+public interface ServerComponent {
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/ServerExtension.java b/sonar-plugin-api/src/main/java/org/sonar/api/ServerExtension.java
new file mode 100644
index 00000000000..e5df1676933
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/ServerExtension.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+/**
+ * Server extension point.
+ *
+ * @since 1.10
+ */
+public interface ServerExtension extends Extension, ServerComponent {
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractCoverageExtension.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractCoverageExtension.java
new file mode 100644
index 00000000000..330e1d4e79c
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractCoverageExtension.java
@@ -0,0 +1,70 @@
+/*
+ * 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.batch;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.sonar.api.BatchExtension;
+import org.sonar.api.Plugins;
+import org.sonar.api.resources.Project;
+
+/**
+ * This class implements the management of the code coverage engine if there are several.
+ * It is a pre-implementation for Sensors and Decorators
+ *
+ * @since 1.10
+ */
+public abstract class AbstractCoverageExtension implements BatchExtension {
+
+ /**
+ * The plugin key to retrieve the coverage engine to be used
+ */
+ public static final String PARAM_PLUGIN = "sonar.core.codeCoveragePlugin";
+
+ /**
+ * The default value for the code coverage plugin
+ */
+ public static final String DEFAULT_PLUGIN = "cobertura";
+
+ private final Plugins plugins;
+
+ /**
+ * Default constructor
+ * @param plugins the list of plugins available
+ */
+ public AbstractCoverageExtension(Plugins plugins) {
+ this.plugins = plugins;
+ }
+
+ /**
+ * Whether to implement the extension on the project
+ */
+ public boolean shouldExecuteOnProject(Project project) {
+ return project.getAnalysisType().isDynamic(true) && isSelectedPlugin(project);
+ }
+
+ protected boolean isSelectedPlugin(Project project) {
+ String[] selectedPluginKeys = project.getConfiguration().getStringArray(PARAM_PLUGIN);
+ if (selectedPluginKeys.length == 0) {
+ selectedPluginKeys = new String[]{DEFAULT_PLUGIN};
+ }
+ String pluginKey = plugins.getPluginKeyByExtension(getClass());
+ return ArrayUtils.contains(selectedPluginKeys, pluginKey);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractCpdMapping.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractCpdMapping.java
new file mode 100644
index 00000000000..552a9e7856d
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractCpdMapping.java
@@ -0,0 +1,40 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.resources.File;
+import org.sonar.api.resources.Resource;
+
+import java.util.List;
+
+/**
+ * A pre-implementation of the CpdMapping extension point
+ *
+ * @since 1.10
+ */
+public abstract class AbstractCpdMapping implements CpdMapping {
+
+ /**
+ * {@inheritDoc}
+ */
+ public Resource createResource(java.io.File file, List<java.io.File> sourceDirs) {
+ return File.fromIOFile(file, sourceDirs);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractDirectoriesDecorator.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractDirectoriesDecorator.java
new file mode 100644
index 00000000000..7ba2f877974
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractDirectoriesDecorator.java
@@ -0,0 +1,55 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.resources.Language;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+
+/**
+ * A pre-implementation to decorate the number of directories
+ *
+ * @since 1.10
+ * @deprecated since 2.2, the number of directories is automatically calculated by sonar core (see metric formula)
+ */
+@Deprecated
+public abstract class AbstractDirectoriesDecorator implements Decorator {
+
+
+ /**
+ * @param language this will be use to defined whether the decorator should be executed on a project
+ */
+ public AbstractDirectoriesDecorator(Language language) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean shouldExecuteOnProject(Project project) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void decorate(Resource resource, DecoratorContext context) {
+
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractDivisionDecorator.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractDivisionDecorator.java
new file mode 100644
index 00000000000..303be1f6ab6
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractDivisionDecorator.java
@@ -0,0 +1,92 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasureUtils;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A pre-implementation to decorate metrics that are the result of a division
+ *
+ * @since 1.10
+ */
+public abstract class AbstractDivisionDecorator implements Decorator {
+
+ protected abstract Metric getQuotientMetric();
+
+ protected abstract Metric getDivisorMetric();
+
+ protected abstract Metric getDividendMetric();
+
+ /**
+ * Used to define upstream dependencies
+ */
+ @DependsUpon
+ public List<Metric> dependsUponMetrics() {
+ return Arrays.asList(getDividendMetric(), getDivisorMetric());
+ }
+
+ /**
+ * Used to define downstream dependencies
+ */
+ @DependedUpon
+ public Metric generatesMetric() {
+ return getQuotientMetric();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (!shouldDecorateResource(context)) {
+ return;
+ }
+ Measure dividend = context.getMeasure(getDividendMetric());
+ Measure divisor = context.getMeasure(getDivisorMetric());
+
+ if (MeasureUtils.hasValue(dividend) && MeasureUtils.hasValue(divisor) && divisor.getValue() > 0.0) {
+ context.saveMeasure(new Measure(getQuotientMetric(), compute(dividend, divisor, getQuotientMetric().isPercentageType())));
+ }
+ }
+
+ protected boolean shouldDecorateResource(DecoratorContext context) {
+ return context.getMeasure(getQuotientMetric()) == null;
+ }
+
+
+ protected double compute(Measure dividend, Measure divisor, boolean shouldResultBeInPercent) {
+ double result = dividend.getValue() / divisor.getValue();
+ return shouldResultBeInPercent ? result * 100 : result;
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractFileComplexityDecorator.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractFileComplexityDecorator.java
new file mode 100644
index 00000000000..37945f608bd
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractFileComplexityDecorator.java
@@ -0,0 +1,88 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.MeasureUtils;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Language;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @deprecated a formula has been implemented on the metric, so no need to have decorator anymore
+ * @since 2.1
+ */
+public abstract class AbstractFileComplexityDecorator implements Decorator {
+
+ private Language language;
+
+ /**
+ * @param language this will be use to defined whether the decorator should be executed on a project
+ */
+ public AbstractFileComplexityDecorator(Language language) {
+ this.language = language;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean shouldExecuteOnProject(Project project) {
+ return language.equals(project.getLanguage());
+ }
+
+ /**
+ * Used to define upstream dependencies
+ */
+ @DependsUpon
+ public List<Metric> dependsUponFileAndComplexityMetrics() {
+ return Arrays.asList(CoreMetrics.FILES, CoreMetrics.COMPLEXITY);
+ }
+
+ /**
+ * Used to define downstream dependencies
+ */
+ @DependedUpon
+ public Metric generateFileComplexityMetric() {
+ return CoreMetrics.FILE_COMPLEXITY;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (!shouldDecorateResource(resource, context)) {
+ return;
+ }
+ Double files = MeasureUtils.getValue(context.getMeasure(CoreMetrics.FILES), null);
+ Double complexity = MeasureUtils.getValue(context.getMeasure(CoreMetrics.COMPLEXITY), null);
+ if (complexity != null && files != null && files > 0.0) {
+ context.saveMeasure(CoreMetrics.FILE_COMPLEXITY, complexity / files);
+ }
+ }
+
+ private boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
+ return !MeasureUtils.hasValue(context.getMeasure(CoreMetrics.FILE_COMPLEXITY)) && !ResourceUtils.isEntity(resource);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractFilesDecorator.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractFilesDecorator.java
new file mode 100644
index 00000000000..8c64927a433
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractFilesDecorator.java
@@ -0,0 +1,55 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.resources.Language;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+
+/**
+ * A pre-implementation to decorate the number of files
+
+ * @since 1.10
+ * @deprecated since 2.2, the number of files is automatically calculated by sonar core (see metric formula)
+ */
+@Deprecated
+public abstract class AbstractFilesDecorator implements Decorator {
+
+
+ /**
+ * @param language this will be use to defined whether the decorator should be executed on a project
+ */
+ public AbstractFilesDecorator(Language language) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean shouldExecuteOnProject(Project project) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void decorate(Resource resource, DecoratorContext context) {
+
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractFunctionComplexityDecorator.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractFunctionComplexityDecorator.java
new file mode 100644
index 00000000000..1a17608f1d7
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractFunctionComplexityDecorator.java
@@ -0,0 +1,87 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.MeasureUtils;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Language;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @deprecated a formula has been implemented on the metric, so no need to have decorator anymore
+ * @since 2.1
+ */
+public abstract class AbstractFunctionComplexityDecorator implements Decorator {
+
+ private Language language;
+
+ /**
+ * @param language this will be use to defined whether the decorator should be executed on a project
+ */
+ public AbstractFunctionComplexityDecorator(Language language) {
+ this.language = language;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean shouldExecuteOnProject(Project project) {
+ return language.equals(project.getLanguage());
+ }
+
+ /**
+ * Used to define upstream dependencies
+ */
+ @DependsUpon
+ public List<Metric> dependsUponFileAndComplexityMetrics() {
+ return Arrays.asList(CoreMetrics.FUNCTIONS, CoreMetrics.COMPLEXITY);
+ }
+
+ /**
+ * Used to define downstream dependencies
+ */
+ @DependedUpon
+ public Metric generateFileComplexityMetric() {
+ return CoreMetrics.FUNCTION_COMPLEXITY;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (!shouldDecorateResource(resource, context)) {
+ return;
+ }
+ Double functions = MeasureUtils.getValue(context.getMeasure(CoreMetrics.FUNCTIONS), null);
+ Double complexity = MeasureUtils.getValue(context.getMeasure(CoreMetrics.COMPLEXITY), null);
+ if (complexity != null && functions != null && functions > 0.0) {
+ context.saveMeasure(CoreMetrics.FUNCTION_COMPLEXITY, complexity / functions);
+ }
+ }
+
+ private boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
+ return !MeasureUtils.hasValue(context.getMeasure(CoreMetrics.FUNCTION_COMPLEXITY));
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractFunctionComplexityDistributionDecorator.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractFunctionComplexityDistributionDecorator.java
new file mode 100644
index 00000000000..e6323ffa720
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractFunctionComplexityDistributionDecorator.java
@@ -0,0 +1,83 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.measures.CountDistributionBuilder;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.Language;
+
+
+/**
+ * @deprecated a formula has been implemented on the metric, so no need to have decorator anymore
+ * @since 2.1
+ */
+public abstract class AbstractFunctionComplexityDistributionDecorator implements Decorator {
+
+ private CountDistributionBuilder builder = new CountDistributionBuilder(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION);
+ private Language language;
+
+ public AbstractFunctionComplexityDistributionDecorator(Language language) {
+ this.language = language;
+ }
+
+ @DependedUpon
+ public Metric generatesMetrics() {
+ return CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return language.equals(project.getLanguage());
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (shouldDecorateResource(context)) {
+ reset();
+ saveDistribution(context);
+ }
+ }
+
+ private void saveDistribution(DecoratorContext context) {
+ for (Measure childMeasure : context.getChildrenMeasures(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION)) {
+ builder.add(childMeasure);
+ }
+
+ if (!builder.isEmpty()) {
+ context.saveMeasure(builder.build());
+ }
+ }
+
+ private void reset() {
+ builder.clear();
+ }
+
+ private boolean shouldDecorateResource(DecoratorContext context) {
+ return context.getMeasure(CoreMetrics.FUNCTION_COMPLEXITY_DISTRIBUTION) == null;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractSourceImporter.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractSourceImporter.java
new file mode 100644
index 00000000000..f5a179e328d
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractSourceImporter.java
@@ -0,0 +1,114 @@
+/*
+ * 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.batch;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.List;
+
+import org.apache.commons.io.FileUtils;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.resources.Language;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.ProjectFileSystem;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.utils.SonarException;
+
+/**
+ * A pre-implementation for a sensor that imports sources
+ *
+ * @since 1.10
+ */
+@Phase(name = Phase.Name.PRE)
+public abstract class AbstractSourceImporter implements Sensor {
+
+ /**
+ * @deprecated replaced by CoreProperties.CORE_IMPORT_SOURCES_PROPERTY since 1.11
+ */
+ @Deprecated
+ public static final String KEY_IMPORT_SOURCES = "sonar.importSources";
+
+ /**
+ * @deprecated replaced by CoreProperties.CORE_IMPORT_SOURCES_DEFAULT_VALUE since 1.11
+ */
+ @Deprecated
+ public static final boolean DEFAULT_IMPORT_SOURCES = true;
+
+ private Language language;
+
+ public AbstractSourceImporter(Language language) {
+ this.language = language;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean shouldExecuteOnProject(Project project) {
+ return isEnabled(project) && language.equals(project.getLanguage());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void analyse(Project project, SensorContext context) {
+ analyse(project.getFileSystem(), context);
+ }
+
+ protected void analyse(ProjectFileSystem fileSystem, SensorContext context) {
+ parseDirs(context, fileSystem.getSourceFiles(language), fileSystem.getSourceDirs(), false, fileSystem.getSourceCharset());
+ parseDirs(context, fileSystem.getTestFiles(language), fileSystem.getTestDirs(), true, fileSystem.getSourceCharset());
+ }
+
+ protected void parseDirs(SensorContext context, List<File> files, List<File> sourceDirs, boolean unitTest, Charset sourcesEncoding) {
+ for (File file : files) {
+ Resource resource = createResource(file, sourceDirs, unitTest);
+ if (resource != null) {
+ try {
+ String source = FileUtils.readFileToString(file, sourcesEncoding.name());
+ context.saveSource(resource, source);
+ } catch (IOException e) {
+ throw new SonarException("Unable to read and import the source file : '" + file.getAbsolutePath() + "' with the charset : '"
+ + sourcesEncoding.name() + "'.", e);
+ }
+ }
+ }
+ }
+
+ protected Resource createResource(File file, List<File> sourceDirs, boolean unitTest) {
+ org.sonar.api.resources.File resource = org.sonar.api.resources.File.fromIOFile(file, sourceDirs);
+ if (resource != null) {
+ resource.setLanguage(language);
+ }
+ return resource;
+ }
+
+ protected boolean isEnabled(Project project) {
+ return project.getConfiguration().getBoolean(CoreProperties.CORE_IMPORT_SOURCES_PROPERTY,
+ CoreProperties.CORE_IMPORT_SOURCES_DEFAULT_VALUE);
+ }
+
+ /**
+ * @return the language
+ */
+ public Language getLanguage() {
+ return language;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractSumChildrenDecorator.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractSumChildrenDecorator.java
new file mode 100644
index 00000000000..0bf31ec6289
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractSumChildrenDecorator.java
@@ -0,0 +1,89 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasureUtils;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+
+import java.util.List;
+
+/**
+ * Sum measures of child resources.
+ *
+ * @since 1.10
+ */
+public abstract class AbstractSumChildrenDecorator implements Decorator {
+
+
+ /**
+ * Each metric is used individually. There are as many generated measures than metrics.
+ * <p/>
+ * <p><b>Important</b> : annotations are not inherited, so you have to copy the @DependedUpon annotation
+ * when implementing this method.</p>
+ *
+ * @return not null list of metrics
+ */
+ @DependedUpon
+ public abstract List<Metric> generatesMetrics();
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ /**
+ * @return whether it should save zero if no child measures
+ */
+ protected abstract boolean shouldSaveZeroIfNoChildMeasures();
+
+ /**
+ * {@inheritDoc}
+ */
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (!shouldDecorateResource(resource)) {
+ return;
+ }
+ for (Metric metric : generatesMetrics()) {
+ if (context.getMeasure(metric) == null) {
+ Double sum = MeasureUtils.sum(shouldSaveZeroIfNoChildMeasures(), context.getChildrenMeasures(metric));
+ if (sum != null) {
+ context.saveMeasure(new Measure(metric, sum));
+ }
+ }
+ }
+ }
+
+ /**
+ * @return whether the resource should be decorated or not
+ */
+ public boolean shouldDecorateResource(Resource resource) {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractViolationsStaxParser.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractViolationsStaxParser.java
new file mode 100644
index 00000000000..d944d156a32
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/AbstractViolationsStaxParser.java
@@ -0,0 +1,192 @@
+/*
+ * 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.batch;
+
+import static org.apache.commons.lang.StringUtils.isNotBlank;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.text.ParseException;
+
+import javax.xml.stream.XMLStreamException;
+
+import org.apache.commons.io.IOUtils;
+import org.codehaus.staxmate.in.SMEvent;
+import org.codehaus.staxmate.in.SMHierarchicCursor;
+import org.codehaus.staxmate.in.SMInputCursor;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RulesManager;
+import org.sonar.api.rules.Violation;
+import org.sonar.api.utils.ParsingUtils;
+import org.sonar.api.utils.StaxParser;
+
+/**
+ * @since 1.10
+ */
+public abstract class AbstractViolationsStaxParser {
+
+ protected RulesManager rulesManager;
+ protected SensorContext context;
+ protected boolean doSaveViolationsOnUnexistedResource = true;
+
+ /**
+ * @deprecated since 1.11.
+ */
+ @Deprecated
+ protected AbstractViolationsStaxParser(SensorContext context, RulesManager rulesManager, RulesProfile profile) {
+ this.rulesManager = rulesManager;
+ this.context = context;
+ }
+
+ protected AbstractViolationsStaxParser(SensorContext context, RulesManager rulesManager) {
+ this.rulesManager = rulesManager;
+ this.context = context;
+ }
+
+ /**
+ * Cursor for child resources to parse, the returned input cursor should be filtered on SMEvent.START_ELEMENT for optimal perfs
+ *
+ * @param rootCursor
+ * the root xml doc cursor
+ * @return a cursor with child resources elements to parse
+ */
+ protected abstract SMInputCursor cursorForResources(SMInputCursor rootCursor) throws XMLStreamException;
+
+ /**
+ * Cursor for violations to parse for a given resource, the returned input cursor should be filtered on SMEvent.START_ELEMENT for optimal
+ * perfs
+ *
+ * @param resourcesCursor
+ * the current resource cursor
+ * @return a cursor with child violations elements to parse
+ */
+ protected abstract SMInputCursor cursorForViolations(SMInputCursor resourcesCursor) throws XMLStreamException;
+
+ /**
+ * Transforms a given xml resource to a resource Object
+ */
+ protected abstract Resource toResource(SMInputCursor resourceCursor) throws XMLStreamException;
+
+ protected abstract String messageFor(SMInputCursor violationCursor) throws XMLStreamException;
+
+ protected abstract String ruleKey(SMInputCursor violationCursor) throws XMLStreamException;
+
+ protected abstract String keyForPlugin();
+
+ protected abstract String lineNumberForViolation(SMInputCursor violationCursor) throws XMLStreamException;
+
+ /**
+ * Specify if violations must be saved even if when the Resource associated to a violation doesn't yet exist.
+ * In that case the Resource is automatically created.
+ *
+ * @param doSaveViolationsOnUnexistedResource by default, the value is true
+ */
+ public final void setDoSaveViolationsOnUnexistedResource(boolean doSaveViolationsOnUnexistedResource) {
+ this.doSaveViolationsOnUnexistedResource = doSaveViolationsOnUnexistedResource;
+ }
+
+ public void parse(File violationsXMLFile) throws XMLStreamException {
+ if (violationsXMLFile != null && violationsXMLFile.exists()) {
+ InputStream input = null;
+ try {
+ input = new FileInputStream(violationsXMLFile);
+ parse(input);
+
+ } catch (FileNotFoundException e) {
+ throw new XMLStreamException(e);
+
+ } finally {
+ IOUtils.closeQuietly(input);
+ }
+ }
+ }
+
+ public final void parse(InputStream input) throws XMLStreamException {
+ if (input != null) {
+ StaxParser parser = new StaxParser(new StaxParser.XmlStreamHandler() {
+
+ public void stream(SMHierarchicCursor rootCursor) throws XMLStreamException {
+ parseResources(rootCursor.advance());
+ }
+ }, true);
+ parser.parse(input);
+ }
+ }
+
+ private void parseResources(SMInputCursor rootCursor) throws XMLStreamException {
+ SMInputCursor resourcesCursor = cursorForResources(rootCursor);
+ SMEvent event;
+ while ((event = resourcesCursor.getNext()) != null) {
+ if (event.compareTo(SMEvent.START_ELEMENT) == 0) {
+ parseViolations(resourcesCursor);
+ }
+ }
+ }
+
+ private void parseViolations(SMInputCursor resourcesCursor) throws XMLStreamException {
+ Resource resource = toResource(resourcesCursor);
+ if ( !doSaveViolationsOnUnexistedResource && context.getResource(resource) == null) {
+ return;
+ }
+ SMInputCursor violationsCursor = cursorForViolations(resourcesCursor);
+ SMEvent event;
+ while ((event = violationsCursor.getNext()) != null) {
+ if (event.compareTo(SMEvent.START_ELEMENT) == 0) {
+ createViolationFor(resource, violationsCursor);
+ }
+ }
+ }
+
+ private void createViolationFor(Resource resource, SMInputCursor violationCursor) throws XMLStreamException {
+ Rule rule = getRule(violationCursor);
+ Integer line = getLineIndex(violationCursor);
+ if (rule != null && resource != null) {
+ Violation violation = new Violation(rule, resource)
+ .setLineId(line)
+ .setMessage(messageFor(violationCursor));
+ context.saveViolation(violation);
+ }
+ }
+
+ private Rule getRule(SMInputCursor violationCursor) throws XMLStreamException {
+ return rulesManager.getPluginRule(keyForPlugin(), ruleKey(violationCursor));
+ }
+
+ private Integer getLineIndex(SMInputCursor violationCursor) throws XMLStreamException {
+ String line = lineNumberForViolation(violationCursor);
+ return parseLineIndex(line);
+ }
+
+ protected static Integer parseLineIndex(String line) {
+ if ( !isNotBlank(line) || line.indexOf('-') != -1) {
+ return null;
+ }
+ try {
+ return (int) ParsingUtils.parseNumber(line);
+ } catch (ParseException ignore) {
+ return null;
+ }
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java
new file mode 100644
index 00000000000..1ebb6654a8f
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/BatchExtensionDictionnary.java
@@ -0,0 +1,227 @@
+/*
+ * 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.batch;
+
+import com.google.common.base.Predicates;
+import com.google.common.collect.Collections2;
+import org.apache.commons.lang.ClassUtils;
+import org.picocontainer.MutablePicoContainer;
+import org.sonar.api.BatchExtension;
+import org.sonar.api.batch.maven.DependsUponMavenPlugin;
+import org.sonar.api.batch.maven.MavenPluginHandler;
+import org.sonar.api.resources.Project;
+import org.sonar.api.utils.AnnotationUtils;
+import org.sonar.api.utils.IocContainer;
+import org.sonar.api.utils.dag.DirectAcyclicGraph;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @since 1.11
+ */
+public class BatchExtensionDictionnary {
+
+ private MutablePicoContainer picoContainer;
+
+ public BatchExtensionDictionnary(IocContainer iocContainer) {
+ this.picoContainer = iocContainer.getPicoContainer();
+ }
+
+ public BatchExtensionDictionnary(MutablePicoContainer picoContainer) {
+ this.picoContainer = picoContainer;
+ }
+
+ public <T> Collection<T> select(Class<T> type) {
+ return select(type, null, false);
+ }
+
+ public <T> Collection<T> select(Class<T> type, Project project, boolean sort) {
+ List<T> result = getFilteredExtensions(type, project);
+ if (sort) {
+ return sort(result);
+ }
+ return result;
+ }
+
+ public Collection<MavenPluginHandler> selectMavenPluginHandlers(Project project) {
+ Collection<DependsUponMavenPlugin> selectedExtensions = select(DependsUponMavenPlugin.class, project, true);
+ List<MavenPluginHandler> handlers = new ArrayList<MavenPluginHandler>();
+ for (DependsUponMavenPlugin extension : selectedExtensions) {
+ MavenPluginHandler handler = extension.getMavenPluginHandler(project);
+ if (handler != null) {
+ boolean ok = true;
+ if (handler instanceof CheckProject) {
+ ok = ((CheckProject) handler).shouldExecuteOnProject(project);
+ }
+ if (ok) {
+ handlers.add(handler);
+ }
+ }
+
+ }
+ return handlers;
+ }
+
+ private List<BatchExtension> getExtensions() {
+ return picoContainer.getComponents(BatchExtension.class);
+ }
+
+ private <T> List<T> getFilteredExtensions(Class<T> type, Project project) {
+ List<T> result = new ArrayList<T>();
+ for (BatchExtension extension : getExtensions()) {
+ if (shouldKeep(type, extension, project)) {
+ result.add((T) extension);
+ }
+ }
+ return result;
+ }
+
+ private boolean shouldKeep(Class type, Object extension, Project project) {
+ boolean keep = ClassUtils.isAssignable(extension.getClass(), type);
+ if (keep && project != null && ClassUtils.isAssignable(extension.getClass(), CheckProject.class)) {
+ keep = ((CheckProject) extension).shouldExecuteOnProject(project);
+ }
+ return keep;
+ }
+
+ public <T> Collection<T> sort(Collection<T> extensions) {
+ DirectAcyclicGraph dag = new DirectAcyclicGraph();
+
+ for (T extension : extensions) {
+ dag.add(extension);
+ for (Object dependency : getDependencies(extension)) {
+ dag.add(extension, dependency);
+ }
+ for (Object generates : getDependents(extension)) {
+ dag.add(generates, extension);
+ }
+ completePhaseDependencies(dag, extension);
+ }
+ List sortedList = dag.sort();
+
+ return (Collection<T>) Collections2.filter(sortedList, Predicates.in(extensions));
+ }
+
+ /**
+ * Extension dependencies
+ */
+ private <T> List getDependencies(T extension) {
+ return evaluateAnnotatedClasses(extension, DependsUpon.class);
+ }
+
+ /**
+ * Objects that depend upon this extension.
+ */
+ public <T> List getDependents(T extension) {
+ return evaluateAnnotatedClasses(extension, DependedUpon.class);
+ }
+
+ private void completePhaseDependencies(DirectAcyclicGraph dag, Object extension) {
+ Phase.Name phase = evaluatePhase(extension);
+ dag.add(extension, phase);
+ for (Phase.Name name : Phase.Name.values()) {
+ if (phase.compareTo(name) < 0) {
+ dag.add(name, extension);
+ } else if (phase.compareTo(name) > 0) {
+ dag.add(extension, name);
+ }
+ }
+ }
+
+
+ protected List evaluateAnnotatedClasses(Object extension, Class annotation) {
+ List results = new ArrayList();
+ Class aClass = extension.getClass();
+ while (aClass != null) {
+ evaluateClass(aClass, annotation, results);
+
+ for (Method method : aClass.getDeclaredMethods()) {
+ if (method.getAnnotation(annotation) != null) {
+ checkAnnotatedMethod(method);
+ evaluateMethod(extension, method, results);
+ }
+ }
+ aClass = aClass.getSuperclass();
+ }
+
+ return results;
+ }
+
+ private void evaluateClass(Class extensionClass, Class annotationClass, List results) {
+ Annotation annotation = extensionClass.getAnnotation(annotationClass);
+ if (annotation != null) {
+ if (annotation.annotationType().isAssignableFrom(DependsUpon.class)) {
+ results.addAll(Arrays.asList(((DependsUpon) annotation).value()));
+ results.addAll(Arrays.asList(((DependsUpon) annotation).classes()));
+ } else if (annotation.annotationType().isAssignableFrom(DependedUpon.class)) {
+ results.addAll(Arrays.asList(((DependedUpon) annotation).value()));
+ results.addAll(Arrays.asList(((DependedUpon) annotation).classes()));
+ }
+ }
+
+ Class[] interfaces = extensionClass.getInterfaces();
+ for (Class anInterface : interfaces) {
+ evaluateClass(anInterface, annotationClass, results);
+ }
+ }
+
+ protected Phase.Name evaluatePhase(Object extension) {
+ Phase phaseAnnotation = AnnotationUtils.getClassAnnotation(extension, Phase.class);
+ if (phaseAnnotation != null) {
+ return phaseAnnotation.name();
+ }
+ return Phase.Name.DEFAULT;
+ }
+
+ private void evaluateMethod(Object extension, Method method, List results) {
+ try {
+ Object result = method.invoke(extension);
+ if (result != null) {
+ //TODO add arrays/collections of objects/classes
+ if (result instanceof Class) {
+ results.addAll(picoContainer.getComponents((Class) result));
+
+ } else if (result instanceof Collection) {
+ results.addAll((Collection) result);
+
+ } else {
+ results.add(result);
+ }
+ }
+ } catch (Exception e) {
+ throw new IllegalStateException("Can not invoke method " + method, e);
+ }
+ }
+
+ private void checkAnnotatedMethod(Method method) {
+ if (!Modifier.isPublic(method.getModifiers())) {
+ throw new IllegalStateException("Annotated method must be public :" + method);
+ }
+ if (method.getParameterTypes().length > 0) {
+ throw new IllegalStateException("Annotated method must not have parameters :" + method);
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/BuildBreaker.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/BuildBreaker.java
new file mode 100644
index 00000000000..f9b78d9291e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/BuildBreaker.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.batch;
+
+import org.sonar.api.utils.SonarException;
+
+/**
+ * @since 1.10
+ */
+@Phase(name = Phase.Name.POST)
+public abstract class BuildBreaker implements PostJob {
+
+ /**
+ * Execute this method to fail the build.
+ */
+ protected void fail(String message) {
+ throw new SonarException(message);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/CheckProject.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/CheckProject.java
new file mode 100644
index 00000000000..eda71d16176
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/CheckProject.java
@@ -0,0 +1,31 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.resources.Project;
+
+/**
+ * @since 1.10
+ */
+public interface CheckProject {
+
+ boolean shouldExecuteOnProject(Project project);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/CpdMapping.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/CpdMapping.java
new file mode 100644
index 00000000000..cd2d644af05
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/CpdMapping.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.batch;
+
+import net.sourceforge.pmd.cpd.Tokenizer;
+import org.sonar.api.BatchExtension;
+import org.sonar.api.resources.Language;
+import org.sonar.api.resources.Resource;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * @since 1.10
+ */
+public interface CpdMapping extends BatchExtension {
+
+ Tokenizer getTokenizer();
+
+ Language getLanguage();
+
+ Resource createResource(File file, List<File> sourceDirs);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/Decorator.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Decorator.java
new file mode 100644
index 00000000000..fa2e9bf7d51
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Decorator.java
@@ -0,0 +1,32 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.BatchExtension;
+import org.sonar.api.resources.Resource;
+
+/**
+ * @since 1.10
+ */
+public interface Decorator extends BatchExtension, CheckProject {
+
+ void decorate(Resource resource, DecoratorContext context);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/DecoratorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/DecoratorContext.java
new file mode 100644
index 00000000000..d8fa8938406
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/DecoratorContext.java
@@ -0,0 +1,144 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.design.Dependency;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasuresFilter;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.Violation;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @since 1.10
+ */
+public interface DecoratorContext {
+
+ /**
+ * @return the project in which the decorator is
+ */
+ Project getProject();
+
+ /**
+ * @return the resource that is currently decorated
+ */
+ Resource getResource();
+
+ /**
+ * Child contexts are read only
+ */
+
+ List<DecoratorContext> getChildren();
+
+
+ // MEASURES
+
+ /**
+ * Find a measure for the resource
+ */
+ Measure getMeasure(Metric metric);
+
+ /**
+ * Never return null.
+ */
+ <M> M getMeasures(MeasuresFilter<M> filter);
+
+ /**
+ * Never return null.
+ */
+ Collection<Measure> getChildrenMeasures(MeasuresFilter filter);
+
+ /**
+ * @return the resource children measures for the given metric
+ */
+ Collection<Measure> getChildrenMeasures(Metric metric);
+
+
+ /**
+ * Add a measure on the current resource. It can not be executed from children contexts.
+ *
+ * @return the same context
+ */
+ DecoratorContext saveMeasure(Measure measure);
+
+ /**
+ * Add a measure on the current resource. It can not be executed from children contexts.
+ *
+ * @return the current object
+ */
+ DecoratorContext saveMeasure(Metric metric, Double value);
+
+
+ Dependency saveDependency(Dependency dependency);
+
+ Set<Dependency> getDependencies();
+
+ Collection<Dependency> getIncomingDependencies();
+
+ Collection<Dependency> getOutgoingDependencies();
+
+
+ // RULES
+
+ /**
+ * Read-only rule failures.
+ *
+ * @return the rule failures for file/classes resources, null for the others
+ */
+ List<Violation> getViolations();
+
+
+ /**
+ * Save a coding rule violation. The decorator which calls this method must implement org.sonar.api.batch.GeneratesViolations
+ */
+ DecoratorContext saveViolation(Violation violation);
+
+ /**
+ * @return the list of events associated to the current resource
+ */
+ List<Event> getEvents();
+
+ /**
+ * Creates an event for a given date
+ *
+ * @param name the event name
+ * @param description the event description
+ * @param category the event category
+ * @param date the event date
+ * @return the created event
+ */
+ Event createEvent(String name, String description, String category, Date date);
+
+ /**
+ * Deletes an event
+ *
+ * @param event the event to delete
+ */
+ void deleteEvent(Event event);
+
+
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/DefaultFormulaContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/DefaultFormulaContext.java
new file mode 100644
index 00000000000..fc93120794c
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/DefaultFormulaContext.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.api.batch;
+
+import org.apache.commons.configuration.Configuration;
+import org.sonar.api.measures.FormulaContext;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Resource;
+
+/**
+ * @since 1.11
+ */
+public class DefaultFormulaContext implements FormulaContext {
+
+ private Metric metric;
+ private DecoratorContext decoratorContext;
+
+ public DefaultFormulaContext(Metric metric) {
+ this.metric = metric;
+ }
+
+ public Metric getTargetMetric() {
+ return metric;
+ }
+
+ public Resource getResource() {
+ return decoratorContext.getResource();
+ }
+
+ public Configuration getConfiguration() {
+ return decoratorContext.getProject().getConfiguration();
+ }
+
+ public void setMetric(Metric metric) {
+ this.metric = metric;
+ }
+
+ public void setDecoratorContext(DecoratorContext decoratorContext) {
+ this.decoratorContext = decoratorContext;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/DefaultFormulaData.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/DefaultFormulaData.java
new file mode 100644
index 00000000000..79a4d3ff634
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/DefaultFormulaData.java
@@ -0,0 +1,65 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.measures.FormulaData;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasuresFilter;
+import org.sonar.api.measures.Metric;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @since 1.11
+ */
+public class DefaultFormulaData implements FormulaData {
+
+ private DecoratorContext decoratorContext;
+
+ public DefaultFormulaData(DecoratorContext decoratorContext) {
+ this.decoratorContext = decoratorContext;
+ }
+
+ public Measure getMeasure(Metric metric) {
+ return decoratorContext.getMeasure(metric);
+ }
+
+ public <M> M getMeasures(MeasuresFilter<M> filter) {
+ return decoratorContext.getMeasures(filter);
+ }
+
+ public Collection<Measure> getChildrenMeasures(MeasuresFilter filter) {
+ return decoratorContext.getChildrenMeasures(filter);
+ }
+
+ public Collection<Measure> getChildrenMeasures(Metric metric) {
+ return decoratorContext.getChildrenMeasures(metric);
+ }
+
+ public Collection<FormulaData> getChildren() {
+ List<FormulaData> result = new ArrayList<FormulaData>();
+ for (DecoratorContext childContext : decoratorContext.getChildren()) {
+ result.add(new DefaultFormulaData(childContext));
+ }
+ return result;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/DependedUpon.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/DependedUpon.java
new file mode 100644
index 00000000000..73a8093999e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/DependedUpon.java
@@ -0,0 +1,42 @@
+/*
+ * 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.batch;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @since 1.10
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface DependedUpon {
+
+ /**
+ * Used only on classes. Must be keep empty on methods.
+ */
+ String[] value() default {};
+
+ Class[] classes() default {};
+
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/DependsUpon.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/DependsUpon.java
new file mode 100644
index 00000000000..000c2061454
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/DependsUpon.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.batch;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @since 1.10
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+public @interface DependsUpon {
+
+ /**
+ * Used only on classes. Must be keep empty on methods.
+ */
+ String[] value() default {};
+
+ Class[] classes() default {};
+
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/Event.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Event.java
new file mode 100644
index 00000000000..e89f11b44b0
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Event.java
@@ -0,0 +1,169 @@
+/*
+ * 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.batch;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.database.BaseIdentifiable;
+import org.sonar.api.database.model.Snapshot;
+
+import javax.persistence.*;
+import java.util.Date;
+
+/**
+ * @since 1.10
+ */
+@Entity
+@Table(name = "events")
+public class Event extends BaseIdentifiable {
+ public static final String CATEGORY_VERSION = "Version";
+ public static final String CATEGORY_ALERT = "Alert";
+
+ @Column(name = "name", updatable = true, nullable = true, length = 50)
+ private String name;
+
+ @Column(name = "description", updatable = true, nullable = true, length = 3072)
+ private String description;
+
+ @Column(name = "category", updatable = true, nullable = true, length = 50)
+ private String category;
+
+ @Column(name = "event_date", updatable = true, nullable = false)
+ private Date date;
+
+ @Column(name = "created_at", updatable = true, nullable = true)
+ private Date createdAt;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "snapshot_id", updatable = true, nullable = true)
+ private Snapshot snapshot;
+
+ @Column(name = "resource_id", updatable = true, nullable = true)
+ private Integer resourceId;
+
+ @Column(name = "data", updatable = true, nullable = true, length = 4000)
+ private String data;
+
+ public Event() {
+ }
+
+ public Event(String name, String description, String category, Date date, Integer resourceId) {
+ this.name = name;
+ this.description = description;
+ this.category = category;
+ this.date = date;
+ this.resourceId = resourceId;
+ }
+
+ public Event(String name, String description, String category, Snapshot snapshot) {
+ this.name = name;
+ this.description = description;
+ this.category = category;
+ setSnapshot(snapshot);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public void setCategory(String category) {
+ this.category = category;
+ }
+
+ public boolean isVersionCategory() {
+ return CATEGORY_VERSION.equalsIgnoreCase(category);
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public Snapshot getSnapshot() {
+ return snapshot;
+ }
+
+ public Date getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Date createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public final void setSnapshot(Snapshot snapshot) {
+ this.snapshot = snapshot;
+ if (snapshot != null) {
+ this.date = snapshot.getCreatedAt();
+ this.resourceId = snapshot.getResourceId();
+ }
+ }
+
+ public Integer getResourceId() {
+ return resourceId;
+ }
+
+ public Event setResourceId(Integer resourceId) {
+ this.resourceId = resourceId;
+ return this;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public Event setData(String data) {
+ this.data = data;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("name", name)
+ .append("categ", category)
+ .append("date", date)
+ .append("snapshot", snapshot)
+ .append("resource", resourceId)
+ .toString();
+ }
+
+ public boolean isLinkedToSnapshot() {
+ return snapshot != null;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/FormulaDecorator.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/FormulaDecorator.java
new file mode 100644
index 00000000000..9822df5b3e6
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/FormulaDecorator.java
@@ -0,0 +1,119 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.measures.FormulaData;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+
+import java.util.List;
+
+/**
+ * A pre-implementation of a decorator using a simple calculation formula
+ * @since 1.11
+ */
+public class FormulaDecorator implements Decorator {
+
+ private Metric metric;
+ private DefaultFormulaContext formulaContext;
+
+ /**
+ * Creates a FormulaDecorator
+ *
+ * @param metric the metric should have an associated formula
+ *
+ * @throws IllegalArgumentException if no formula is associated to the metric
+ */
+ public FormulaDecorator(Metric metric) {
+ if (metric.getFormula() == null) {
+ throw new IllegalArgumentException("No formula defined on metric");
+ }
+ this.metric = metric;
+ this.formulaContext = new DefaultFormulaContext(metric);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ /**
+ * @return metric generated by the decorator
+ */
+ @DependedUpon
+ public Metric generatesMetric() {
+ return metric;
+ }
+
+ /**
+ * @return metric the decorator depends upon
+ */
+ @DependsUpon
+ public List<Metric> dependsUponMetrics() {
+ return metric.getFormula().dependsUponMetrics();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (context.getMeasure(metric) != null) {
+ return;
+ }
+
+ formulaContext.setDecoratorContext(context);
+ FormulaData data = new DefaultFormulaData(context);
+ Measure measure = metric.getFormula().calculate(data, formulaContext);
+ if (measure != null) {
+ context.saveMeasure(measure);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ FormulaDecorator that = (FormulaDecorator) o;
+
+ if (metric != null ? !metric.equals(that.metric) : that.metric != null) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return metric != null ? metric.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder().append("f(").append(metric).append(")").toString();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/GeneratesViolations.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/GeneratesViolations.java
new file mode 100644
index 00000000000..1e6b1079236
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/GeneratesViolations.java
@@ -0,0 +1,26 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.rules.ViolationFilter;
+
+@DependsUpon(classes = ViolationFilter.class)
+public interface GeneratesViolations {
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/Phase.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Phase.java
new file mode 100644
index 00000000000..cba406cc73e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Phase.java
@@ -0,0 +1,39 @@
+/*
+ * 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.batch;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @since 1.10
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface Phase {
+
+ Name name();
+
+ public enum Name {
+ PRE, DEFAULT, POST
+ }
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/PostJob.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/PostJob.java
new file mode 100644
index 00000000000..1b7f2285b6c
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/PostJob.java
@@ -0,0 +1,38 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.BatchExtension;
+import org.sonar.api.resources.Project;
+
+/**
+ * PostJobs are executed when project is analysed.
+ * <p/>
+ * <p>
+ * Note : executed only on root project, not on modules.
+ * </p>
+ *
+ * @since 1.10
+ */
+public interface PostJob extends BatchExtension {
+
+ void executeOn(Project project, SensorContext context);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/ProjectClasspath.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/ProjectClasspath.java
new file mode 100644
index 00000000000..18a86fd9893
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/ProjectClasspath.java
@@ -0,0 +1,97 @@
+/*
+ * 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.batch;
+
+import org.apache.maven.artifact.DependencyResolutionRequiredException;
+import org.apache.maven.project.MavenProject;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.utils.SonarException;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @since 2.2
+ */
+public class ProjectClasspath implements BatchComponent {
+
+ private MavenProject pom;
+ private List<File> elements;
+ private URLClassLoader classloader;
+
+ public ProjectClasspath(MavenProject pom) {
+ this.pom = pom;
+ }
+
+ public URLClassLoader getClassloader() {
+ if (classloader == null) {
+ classloader = createClassLoader();
+ }
+ return classloader;
+ }
+
+ /**
+ * bytecode directory + JARs (dependencies)
+ */
+ public List<File> getElements() {
+ if (elements == null) {
+ elements = createElements();
+ }
+ return elements;
+ }
+
+ protected URLClassLoader createClassLoader() {
+ try {
+ List<URL> urls = new ArrayList<URL>();
+ for (File file : getElements()) {
+ urls.add(file.toURI().toURL());
+ }
+ return new URLClassLoader(urls.toArray(new URL[urls.size()]), null);
+
+ } catch (MalformedURLException e) {
+ throw new SonarException("Fail to create the project classloader. Classpath element is unvalid.", e);
+ }
+ }
+
+ protected List<File> createElements() {
+ try {
+ List<File> files = new ArrayList<File>();
+ if (pom.getCompileClasspathElements() != null) {
+ for (String classPathString : (List<String>) pom.getCompileClasspathElements()) {
+ files.add(new File(classPathString));
+ }
+ }
+
+ if (pom.getBuild().getOutputDirectory() != null) {
+ File outputDirectoryFile = new File(pom.getBuild().getOutputDirectory());
+ if (outputDirectoryFile.exists()) {
+ files.add(outputDirectoryFile);
+ }
+ }
+ return files;
+ } catch (DependencyResolutionRequiredException e) {
+ throw new SonarException("Fail to create the project classloader", e);
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/Purge.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Purge.java
new file mode 100644
index 00000000000..9f78dc2933d
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Purge.java
@@ -0,0 +1,40 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.BatchExtension;
+
+/**
+ * Extension point to purge data.
+ * <p/>
+ * <p>It is executed when the batch finishes.
+ *
+ * @since 1.10
+ */
+public interface Purge extends BatchExtension {
+
+ /**
+ * Snapshots include the current snapshot (flagged as last) and optionally the penultimate one.
+ *
+ * @snapshots never null.
+ */
+ void purge(PurgeContext context);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/PurgeContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/PurgeContext.java
new file mode 100644
index 00000000000..cc6c4f8818e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/PurgeContext.java
@@ -0,0 +1,31 @@
+/*
+ * 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.batch;
+
+/**
+ *
+ * @since 1.10
+ */
+public interface PurgeContext {
+
+ Integer getPreviousSnapshotId();
+
+ Integer getLastSnapshotId();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/ResourceFilter.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/ResourceFilter.java
new file mode 100644
index 00000000000..42216fb46f0
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/ResourceFilter.java
@@ -0,0 +1,39 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.BatchExtension;
+import org.sonar.api.resources.Resource;
+
+/**
+ * Filter resources to save. For example, ignore a resource if its path matches an exclusion pattern (defined on the project)
+ *
+ * If the method start(), without parameters, exists, then it is executed at startup.
+ *
+ * @since 1.12
+ */
+public interface ResourceFilter extends BatchExtension {
+
+ /**
+ * Return true if the resource must be ignored, else it's saved into database.
+ */
+ boolean isIgnored(Resource resource);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/Sensor.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Sensor.java
new file mode 100644
index 00000000000..c39b7fae02e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/Sensor.java
@@ -0,0 +1,63 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.BatchExtension;
+import org.sonar.api.resources.Project;
+
+/**
+ * <p>A Sensor is invoked once during the analysis of a project. The sensor can invoke a maven plugin,
+ * parse a flat file, connect to a web server... For example the Cobertura Sensor invokes the Codehaus Cobertura MOJO.
+ * Then the generated XML file is parsed and used to save the first-level of measures on resources
+ * (project, package or class).</p>
+ *
+ * <p>Sensors are executed first during project analysis. Sensor are generally used to add measure at the
+ * lowest level of the resource tree. A sensor can access and save measures on the whole tree of resources.</p>
+ *
+ * <p>A particular attention should be given to resource exclusion. Sonar already manages exclusions at file level : if
+ * you try to save a measure on a resource that is excluded in the settings, then Sonar will not save the measure.
+ * When handling a plugin or an external tool, you should make sure that exclusions are passed if you are going to get
+ * back consolidated data.</p>
+ *
+ * @since 1.10
+ */
+public interface Sensor extends BatchExtension, CheckProject {
+
+ /**
+ * Sensors that depend upon Squid must declare the following method :
+ * <code>
+ *
+ * @DependsUpon public String dependsUponSquidAnalysis() {
+ * return Sensor.FLAG_SQUID_ANALYSIS;
+ * }
+ * </code>
+ * }
+ */
+ String FLAG_SQUID_ANALYSIS = "squid";
+
+ /**
+ * The method that is going to be run when the sensor is called
+ *
+ * @param project the project the sensor runs on
+ * @param context the context
+ */
+ void analyse(Project project, SensorContext context);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java
new file mode 100644
index 00000000000..c37e30cca4d
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SensorContext.java
@@ -0,0 +1,175 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.design.Dependency;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasuresFilter;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.ProjectLink;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.Violation;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @since 1.10
+ */
+public interface SensorContext {
+
+ // ----------- MEASURES ON PROJECT --------------
+
+ /**
+ * Find a project measure
+ */
+ Measure getMeasure(Metric metric);
+
+ /**
+ * All measures of the project. Never return null.
+ */
+ <M> M getMeasures(MeasuresFilter<M> filter);
+
+ /**
+ * Add a measure on project
+ */
+ Measure saveMeasure(Measure measure);
+
+ /**
+ * Add a measure on project
+ */
+ Measure saveMeasure(Metric metric, Double value);
+
+
+ // ----------- MEASURES ON RESOURCES --------------
+
+ /**
+ * Find a measure for this project
+ */
+ Measure getMeasure(Resource resource, Metric metric);
+
+ /**
+ * Key is updated when saving the resource.
+ *
+ * @return the key as saved in database. Null if the resource is set as excluded.
+ */
+ String saveResource(Resource resource);
+
+ /**
+ * @return the resource saved in sonar index
+ */
+ Resource getResource(Resource resource);
+
+ /**
+ * Find all measures for this project. Never return null.
+ */
+ <M> M getMeasures(Resource resource, MeasuresFilter<M> filter);
+
+ /**
+ * Add or update a measure.
+ * <p/>
+ * <p>The resource is automatically saved, so there is no need to execute the method saveResource(). Does nothing if the resource is set as excluded.</p>
+ */
+ Measure saveMeasure(Resource resource, Metric metric, Double value);
+
+ /**
+ * Add or update a measure.
+ * <p/>
+ * <p>The resource is automatically saved, so there is no need to execute the method saveResource(). Does nothing if the resource is set as excluded.</p>
+ */
+ Measure saveMeasure(Resource resource, Measure measure);
+
+
+ // ----------- RULE VIOLATIONS --------------
+
+ /**
+ * Save a coding rule violation. The sensor which calls this method must implement org.sonar.api.batch.GeneratesViolations
+ */
+ void saveViolation(Violation violation);
+
+ /**
+ * Saves a list of violations
+ */
+ void saveViolations(Collection<Violation> violations);
+
+
+ // ----------- DEPENDENCIES BETWEEN RESOURCES --------------
+
+ /**
+ * Build a new dependency : from depends upon to. The dependency is NOT saved. The method saveDependency() must still be executed.
+ */
+ Dependency saveDependency(Dependency dependency);
+
+ Set<Dependency> getDependencies();
+
+ Collection<Dependency> getIncomingDependencies(Resource to);
+
+ Collection<Dependency> getOutgoingDependencies(Resource from);
+
+ // ----------- FILE SOURCES --------------
+
+ /**
+ * Does nothing if the resource is set as excluded.
+ */
+ void saveSource(Resource resource, String source);
+
+
+ // ----------- LINKS --------------
+
+ /**
+ * add a link to an external page like project homepage, sources (subversion, ...), continuous integration server...
+ * Example : context.addLink(new ProjectLink("maven_site, "Maven site", "http://my.maven.com)
+ */
+ void saveLink(ProjectLink link);
+
+ /**
+ * remove a link. It does not fail if key is unknown.
+ */
+ void deleteLink(String key);
+
+
+ // ----------- EVENTS --------------
+
+ /**
+ * @param resource set null for project events
+ */
+ List<Event> getEvents(Resource resource);
+
+ /**
+ * Creates an event for a given date
+ *
+ * @param name the event name
+ * @param description the event description
+ * @param category the event category
+ * @param date the event date
+ * @return the created event
+ */
+ Event createEvent(Resource resource, String name, String description, String category, Date date);
+
+ /**
+ * Deletes an event
+ *
+ * @param event the event to delete
+ */
+ void deleteEvent(Event event);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java
new file mode 100644
index 00000000000..af87a386ae1
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SonarIndex.java
@@ -0,0 +1,84 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.design.Dependency;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasuresFilter;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.ProjectLink;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.Violation;
+import org.sonar.graph.DirectedGraphAccessor;
+
+import java.util.Collection;
+import java.util.Date;
+import java.util.List;
+import java.util.Set;
+
+public abstract class SonarIndex implements DirectedGraphAccessor<Resource, Dependency> {
+
+ public abstract Project getRootProject();
+
+ public abstract Project getProject();
+
+ public abstract Resource getResource(Resource resource);
+
+ public final Collection<Resource> getResources() {
+ return getVertices();
+ }
+
+ public abstract List<Resource> getChildren(Resource resource);
+
+ public abstract Resource addResource(Resource resource);
+
+ public abstract Measure getMeasure(Resource resource, Metric metric);
+
+ public abstract <M> M getMeasures(Resource resource, MeasuresFilter<M> filter);
+
+ public abstract void setSource(Resource resource, String source);
+
+ public abstract void addViolation(Violation violation);
+
+ public abstract Measure saveMeasure(Resource resource, Measure measure);
+
+ public abstract Dependency saveDependency(Dependency dependency);
+
+ public abstract Set<Dependency> getDependencies();
+
+ public abstract void saveLink(ProjectLink link);
+
+ public abstract void deleteLink(String key);
+
+ public abstract List<Event> getEvents(Resource resource);
+
+ public abstract void deleteEvent(Event event);
+
+ public abstract Event createEvent(Resource resource, String name, String description, String category, Date date);
+
+ public final Collection<Dependency> getOutgoingDependencies(Resource from) {
+ return getOutgoingEdges(from);
+ }
+
+ public final Collection<Dependency> getIncomingDependencies(Resource to) {
+ return getIncomingEdges(to);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SquidSearch.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SquidSearch.java
new file mode 100644
index 00000000000..5d85c3a2cc4
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SquidSearch.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.api.batch;
+
+import java.util.Collection;
+
+import org.sonar.api.BatchExtension;
+import org.sonar.squid.api.Query;
+import org.sonar.squid.api.SourceCode;
+
+/**
+ * The extension point to access the Squid data tree
+ *
+ * @since 1.11
+ */
+public interface SquidSearch extends BatchExtension {
+ /**
+ * Returns a list of SourceCode objects base a set of queries given
+ *
+ * @param query the set of query
+ * @return SourceCode objects
+ */
+ Collection<SourceCode> search(Query... query);
+
+ /**
+ * Returns a SourceObject given its key
+ *
+ * @param key the key
+ * @return SourceCode object
+ */
+ SourceCode search(String key);
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/SquidUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SquidUtils.java
new file mode 100644
index 00000000000..d0ced27f6d9
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/SquidUtils.java
@@ -0,0 +1,52 @@
+/*
+ * 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.batch;
+
+import org.sonar.api.resources.JavaFile;
+import org.sonar.api.resources.JavaPackage;
+
+public final class SquidUtils {
+
+ private SquidUtils() {
+ // only static methods
+ }
+
+ public static JavaFile convertJavaFileKeyFromSquidFormat(String key) {
+ boolean isJavaFile = key.endsWith(".java");
+ if (isJavaFile) {
+ key = key.substring(0, key.length() - ".java".length());
+ }
+
+ String convertedKey = key.replace('/', '.');
+ if (convertedKey.indexOf('.') == -1 && !convertedKey.equals("")) {
+ convertedKey = "[default]." + convertedKey;
+
+ } else if (convertedKey.indexOf('.') == -1) {
+ convertedKey = "[default]";
+ }
+
+ return new JavaFile(convertedKey);
+ }
+
+ public static JavaPackage convertJavaPackageKeyFromSquidFormat(String key) {
+ String convertedKey = key.replace('/', '.');
+ return new JavaPackage(convertedKey);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/TimeMachine.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/TimeMachine.java
new file mode 100644
index 00000000000..e7387c1ad9f
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/TimeMachine.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.api.batch;
+
+import org.sonar.api.BatchComponent;
+import org.sonar.api.measures.Measure;
+
+import java.util.List;
+
+/**
+ * The TimeMachine extension point
+ *
+ * @since 1.10
+ */
+public interface TimeMachine extends BatchComponent {
+
+ /**
+ * Past measures, sorted by date. Returns all fields.
+ * <p/>
+ * <p>Measures of current analysis are not included.</p>
+ */
+ List<Measure> getMeasures(TimeMachineQuery query);
+
+ /**
+ * Past measures sorted by date. Return only a subset of basic fields : [date (java.util.Date), metric (org.sonar.api.measures.Metric), value (Double)].
+ * <p/>
+ * <p>Measures of current analysis are not included.</p>
+ * <p>This method is recommended instead of getMeasures() for performance reasons. It needs less memory.</p>
+ */
+ List<Object[]> getMeasuresFields(TimeMachineQuery query);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/TimeMachineQuery.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/TimeMachineQuery.java
new file mode 100644
index 00000000000..0f88a74a0ff
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/TimeMachineQuery.java
@@ -0,0 +1,210 @@
+/*
+ * 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.batch;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Resource;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * A class to query TimeMachine on a given resource
+ * <p/>
+ * <p>The query is constructed by setting filters on metrics and on dates</p>
+ * <p>It is to be noted that all filters will be applied regardless of their coherence</p>
+ *
+ * @since 1.10
+ */
+public class TimeMachineQuery {
+
+ private Resource resource;
+ private List<Metric> metrics;
+ private Date from;
+ private Date to;
+ private boolean onlyLastAnalysis = false;
+ private boolean fromCurrentAnalysis = false;
+ private boolean toCurrentAnalysis = false;
+
+ /**
+ * <p>Create a TimeMachine query for a given resource
+ * </p>
+ * Apart from the resource the query is empty, i.e. will return all data for the resource
+ *
+ * @param resource the resource
+ */
+ public TimeMachineQuery(Resource resource) {
+ this.resource = resource;
+ }
+
+ /**
+ * @return the resource of the Query
+ */
+ public Resource getResource() {
+ return resource;
+ }
+
+ /**
+ * Sets the resource of the query
+ *
+ * @param resource the resource
+ * @return this
+ */
+ public TimeMachineQuery setResource(Resource resource) {
+ this.resource = resource;
+ return this;
+ }
+
+ /**
+ * @return the metrics beloging to the query
+ */
+ public List<Metric> getMetrics() {
+ return metrics;
+ }
+
+ /**
+ * Sets the metrics to return
+ *
+ * @param metrics the list of metrics
+ * @return this
+ */
+ public TimeMachineQuery setMetrics(List<Metric> metrics) {
+ this.metrics = metrics;
+ return this;
+ }
+
+ /**
+ * Sets the metrics to return
+ *
+ * @param metrics the list of metrics
+ * @return this
+ */
+ public TimeMachineQuery setMetrics(Metric... metrics) {
+ this.metrics = Arrays.asList(metrics);
+ return this;
+ }
+
+ /**
+ * Unsets the metrics
+ *
+ * @return this
+ */
+ public TimeMachineQuery unsetMetrics() {
+ this.metrics = null;
+ return this;
+ }
+
+ /**
+ * @return the from date of the query
+ */
+ public Date getFrom() {
+ return from;
+ }
+
+ /**
+ * Sets the from date to be used in the query
+ *
+ * @param from the from date
+ * @return this
+ */
+ public TimeMachineQuery setFrom(Date from) {
+ this.from = from;
+ return this;
+ }
+
+ /**
+ * @param b whether to use the latest analysis as a from date
+ * @return this
+ */
+ public TimeMachineQuery setFromCurrentAnalysis(boolean b) {
+ this.fromCurrentAnalysis = b;
+ return this;
+ }
+
+ /**
+ * @param b whether to use the latest analysis as a to date
+ * @return this
+ */
+ public TimeMachineQuery setToCurrentAnalysis(boolean b) {
+ this.toCurrentAnalysis = b;
+ return this;
+ }
+
+ /**
+ * @return whether the latest analysis is used as a from date
+ */
+ public boolean isFromCurrentAnalysis() {
+ return fromCurrentAnalysis;
+ }
+
+ /**
+ * @return whether the latest analysis is used as a to date
+ */
+ public boolean isToCurrentAnalysis() {
+ return toCurrentAnalysis;
+ }
+
+ /**
+ * @return the to date of the query
+ */
+ public Date getTo() {
+ return to;
+ }
+
+ /**
+ * Sets the to date to be used in the query
+ *
+ * @param to the to date
+ * @return this
+ */
+ public TimeMachineQuery setTo(Date to) {
+ this.to = to;
+ return this;
+ }
+
+ /**
+ * @return whether to return only the latest analysis
+ */
+ public boolean isOnlyLastAnalysis() {
+ return onlyLastAnalysis;
+ }
+
+ /**
+ *
+ * @param onlyLastAnalysis whether to only return the latest analysis
+ * @return this
+ */
+ public TimeMachineQuery setOnlyLastAnalysis(boolean onlyLastAnalysis) {
+ this.onlyLastAnalysis = onlyLastAnalysis;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("resource", resource)
+ .append("metrics", metrics)
+ .append("from", from)
+ .append("to", to)
+ .toString();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/DependsUponCustomRules.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/DependsUponCustomRules.java
new file mode 100644
index 00000000000..341674cd227
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/DependsUponCustomRules.java
@@ -0,0 +1,28 @@
+/*
+ * 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.batch.maven;
+
+/**
+ * Marker interface for MavenPluginHandlers. If set, classloader of maven plugin includes JARs of custom rule.
+ *
+ * @since 1.10
+ */
+public interface DependsUponCustomRules {
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/DependsUponMavenPlugin.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/DependsUponMavenPlugin.java
new file mode 100644
index 00000000000..4f79260e8e7
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/DependsUponMavenPlugin.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.batch.maven;
+
+import org.sonar.api.BatchExtension;
+import org.sonar.api.resources.Project;
+
+/**
+ * Used for Sensors and PostJobs only.
+ *
+ * @since 1.10
+ */
+public interface DependsUponMavenPlugin extends BatchExtension {
+
+ MavenPluginHandler getMavenPluginHandler(Project project);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/MavenPlugin.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/MavenPlugin.java
new file mode 100644
index 00000000000..ce035ccb45f
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/MavenPlugin.java
@@ -0,0 +1,397 @@
+/*
+ * 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.batch.maven;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.model.ReportPlugin;
+import org.apache.maven.project.MavenProject;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A class to handle maven plugins
+ *
+ * @since 1.10
+ */
+public class MavenPlugin {
+
+ private Plugin plugin;
+ private Xpp3Dom configuration;
+
+ /**
+ * Creates a MavenPlugin based on a Plugin
+ *
+ * @param plugin the plugin
+ */
+ public MavenPlugin(Plugin plugin) {
+ this.plugin = plugin;
+ this.configuration = (Xpp3Dom) plugin.getConfiguration();
+ if (this.configuration == null) {
+ configuration = new Xpp3Dom("configuration");
+ plugin.setConfiguration(this.configuration);
+ }
+ }
+
+ /**
+ * Creates a Maven plugin based on artifact + group + version
+ *
+ * @param groupId the group id
+ * @param artifactId the artifact id
+ * @param version the version
+ */
+ public MavenPlugin(String groupId, String artifactId, String version) {
+ this.plugin = new Plugin();
+ plugin.setGroupId(groupId);
+ plugin.setArtifactId(artifactId);
+ plugin.setVersion(version);
+ configuration = new Xpp3Dom("configuration");
+ plugin.setConfiguration(this.configuration);
+ }
+
+ /**
+ * Sets the maven plugin version
+ *
+ * @param version the version
+ * @return this
+ */
+ public MavenPlugin setVersion(String version) {
+ this.plugin.setVersion(version);
+ return this;
+ }
+
+ /**
+ * @return the underlying plugin
+ */
+ public Plugin getPlugin() {
+ return plugin;
+ }
+
+ /**
+ * Gets a parameter of the plugin based on its key
+ *
+ * @param key the param key
+ * @return the parameter if exist, null otherwise
+ */
+ public String getParameter(String key) {
+ Xpp3Dom node = findNodeWith(key);
+ return node == null ? null : node.getValue();
+ }
+
+ /**
+ * Gets a list of parameters of the plugin from a param key
+ *
+ * @param key the param key
+ * @return an array of parameters if any, an empty array otherwise
+ */
+ public String[] getParameters(String key) {
+ String[] keyParts = StringUtils.split(key, "/");
+ Xpp3Dom node = configuration;
+ for (int i = 0; i < keyParts.length - 1; i++) {
+ node = getOrCreateChild(node, keyParts[i]);
+ }
+ Xpp3Dom[] children = node.getChildren(keyParts[keyParts.length - 1]);
+ String[] result = new String[children.length];
+ for (int i = 0; i < children.length; i++) {
+ result[i] = children[i].getValue();
+ }
+ return result;
+ }
+
+ /**
+ * Sets a parameter for the maven plugin. This will overrides an existing parameter.
+ *
+ * @param key the param key
+ * @param value the param value
+ * @return this
+ */
+ public MavenPlugin setParameter(String key, String value) {
+ checkKeyArgument(key);
+ String[] keyParts = StringUtils.split(key, "/");
+ Xpp3Dom node = configuration;
+ for (String keyPart : keyParts) {
+ node = getOrCreateChild(node, keyPart);
+ }
+ node.setValue(value);
+ return this;
+ }
+
+ /**
+ * Sets a parameter to the maven plugin. Overrides existing parameter only id specified.
+ *
+ * @param key the param key
+ * @param value the param value
+ * @param override whether to override existing parameter
+ */
+ public void setParameter(String key, String value, boolean override) {
+ if (getParameter(key) == null || override) {
+ setParameter(key, value);
+ }
+ }
+
+ /**
+ * Removes all parameters from the maven plugin
+ */
+ public void removeParameters() {
+ configuration = new Xpp3Dom("configuration");
+ plugin.setConfiguration(this.configuration);
+ }
+
+ /**
+ * Adds a parameter to the maven plugin
+ *
+ * @param key the param key
+ * @param value the param value
+ * @return this
+ */
+ public MavenPlugin addParameter(String key, String value) {
+ String[] keyParts = StringUtils.split(key, "/");
+ Xpp3Dom node = configuration;
+ for (int i = 0; i < keyParts.length - 1; i++) {
+ node = getOrCreateChild(node, keyParts[i]);
+ }
+ Xpp3Dom leaf = new Xpp3Dom(keyParts[keyParts.length - 1]);
+ leaf.setValue(value);
+ node.addChild(leaf);
+ return this;
+ }
+
+ private static Xpp3Dom getOrCreateChild(Xpp3Dom node, String key) {
+ Xpp3Dom child = node.getChild(key);
+ if (child == null) {
+ child = new Xpp3Dom(key);
+ node.addChild(child);
+ }
+ return child;
+ }
+
+ /**
+ * Remove a parameter from the maven plugin based on its key
+ *
+ * @param key the param key
+ */
+ public void removeParameter(String key) {
+ Xpp3Dom node = findNodeWith(key);
+ if (node != null) {
+ remove(node);
+ }
+ }
+
+ private Xpp3Dom findNodeWith(String key) {
+ checkKeyArgument(key);
+ String[] keyParts = key.split("/");
+ Xpp3Dom node = configuration;
+ for (String keyPart : keyParts) {
+ node = node.getChild(keyPart);
+ if (node == null) {
+ return null;
+ }
+ }
+ return node;
+ }
+
+ private static void remove(Xpp3Dom node) {
+ Xpp3Dom parent = node.getParent();
+ for (int i = 0; i < parent.getChildCount(); i++) {
+ Xpp3Dom child = parent.getChild(i);
+ if (child.equals(node)) {
+ parent.removeChild(i);
+ break;
+ }
+ }
+ }
+
+ /**
+ * @return whether the maven plugin has got configuration
+ */
+ public boolean hasConfiguration() {
+ return configuration.getChildCount()>0;
+ }
+
+ private static void checkKeyArgument(String key) {
+ if (key == null) {
+ throw new IllegalArgumentException("Parameter 'key' should not be null.");
+ }
+ }
+
+ /**
+ * Registers a plugin in a project pom
+ * <p/>
+ * <p>Adds the plugin if it does not exist or amend its version if it does exist and specified</p>
+ *
+ * @param pom the project pom
+ * @param groupId the plugin group id
+ * @param artifactId the plugin artifact id
+ * @param version the plugin version
+ * @param overrideVersion whether to override the version if the plugin is already registered
+ * @return the registered plugin
+ */
+ public static MavenPlugin registerPlugin(MavenProject pom, String groupId, String artifactId, String version, boolean overrideVersion) {
+ MavenPlugin plugin = getPlugin(pom, groupId, artifactId);
+ if (plugin == null) {
+ plugin = new MavenPlugin(groupId, artifactId, version);
+
+ } else if (overrideVersion) {
+ plugin.setVersion(version);
+ }
+
+ // remove from pom
+ unregisterPlugin(pom, groupId, artifactId);
+
+ // register
+ pom.getBuild().addPlugin(plugin.getPlugin());
+
+ return plugin;
+ }
+
+ /**
+ * Returns a plugin from a pom based on its group id and artifact id
+ * <p/>
+ * <p>It searches in the build section, then the reporting section and finally the pluginManagement section</p>
+ *
+ * @param pom the project pom
+ * @param groupId the plugin group id
+ * @param artifactId the plugin artifact id
+ * @return the plugin if it exists, null otherwise
+ */
+ public static MavenPlugin getPlugin(MavenProject pom, String groupId, String artifactId) {
+ if (pom == null) {
+ return null;
+ }
+ // look for plugin in <build> section
+ Plugin plugin = null;
+ if (pom.getBuildPlugins() != null) {
+ plugin = getPlugin(pom.getBuildPlugins(), groupId, artifactId);
+ }
+
+ // look for plugin in <report> section
+ if (plugin == null && pom.getReportPlugins() != null) {
+ plugin = getReportPlugin(pom.getReportPlugins(), groupId, artifactId);
+ }
+
+ // look for plugin in <pluginManagement> section
+ if (pom.getPluginManagement() != null) {
+ Plugin pluginManagement = getPlugin(pom.getPluginManagement().getPlugins(), groupId, artifactId);
+ if (plugin == null) {
+ plugin = pluginManagement;
+
+ } else if (pluginManagement != null) {
+ if (pluginManagement.getConfiguration() != null) {
+ if (plugin.getConfiguration() == null) {
+ plugin.setConfiguration(pluginManagement.getConfiguration());
+ } else {
+ Xpp3Dom.mergeXpp3Dom((Xpp3Dom) plugin.getConfiguration(), (Xpp3Dom) pluginManagement.getConfiguration());
+ }
+ }
+ if (plugin.getDependencies() == null && pluginManagement.getDependencies() != null) {
+ plugin.setDependencies(pluginManagement.getDependencies());
+ }
+ if (plugin.getVersion() == null) {
+ plugin.setVersion(pluginManagement.getVersion());
+ }
+ }
+ }
+
+ if (plugin != null) {
+ return new MavenPlugin(plugin);
+ }
+ return null;
+ }
+
+ private static Plugin getPlugin(Collection<Plugin> plugins, String groupId, String artifactId) {
+ if (plugins == null) {
+ return null;
+ }
+
+ for (Plugin plugin : plugins) {
+ if (MavenUtils.equals(plugin, groupId, artifactId)) {
+ return plugin;
+ }
+ }
+ return null;
+ }
+
+ private static Plugin getReportPlugin(Collection<ReportPlugin> plugins, String groupId, String artifactId) {
+ if (plugins == null) {
+ return null;
+ }
+
+ for (ReportPlugin plugin : plugins) {
+ if (MavenUtils.equals(plugin, groupId, artifactId)) {
+ return cloneReportPluginToPlugin(plugin);
+ }
+ }
+ return null;
+ }
+
+ private static Plugin cloneReportPluginToPlugin(ReportPlugin reportPlugin) {
+ Plugin plugin = new Plugin();
+ plugin.setGroupId(reportPlugin.getGroupId());
+ plugin.setArtifactId(reportPlugin.getArtifactId());
+ plugin.setVersion(reportPlugin.getVersion());
+ plugin.setConfiguration(reportPlugin.getConfiguration());
+ return plugin;
+ }
+
+ private static void unregisterPlugin(MavenProject pom, String groupId, String artifactId) {
+ if (pom.getPluginManagement() != null && pom.getPluginManagement().getPlugins() != null) {
+ unregisterPlugin(pom.getPluginManagement().getPlugins(), groupId, artifactId);
+ }
+ if (pom.getBuildPlugins() != null && pom.getBuildPlugins() != null) {
+ unregisterPlugin(pom.getBuildPlugins(), groupId, artifactId);
+ }
+ if (pom.getReportPlugins() != null) {
+ unregisterReportPlugin(pom.getReportPlugins(), groupId, artifactId);
+ }
+ }
+
+ private static void unregisterPlugin(List<Plugin> plugins, String groupId, String artifactId) {
+ for (Iterator<Plugin> iterator = plugins.iterator(); iterator.hasNext();) {
+ Plugin p = iterator.next();
+ if (MavenUtils.equals(p, groupId, artifactId)) {
+ iterator.remove();
+ }
+ }
+ }
+
+ private static void unregisterReportPlugin(List<ReportPlugin> plugins, String groupId, String artifactId) {
+ for (Iterator<ReportPlugin> iterator = plugins.iterator(); iterator.hasNext();) {
+ ReportPlugin p = iterator.next();
+ if (MavenUtils.equals(p, groupId, artifactId)) {
+ iterator.remove();
+ }
+ }
+ }
+
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("groupId", plugin.getGroupId())
+ .append("artifactId", plugin.getArtifactId())
+ .append("version", plugin.getVersion())
+ .toString();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/MavenPluginHandler.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/MavenPluginHandler.java
new file mode 100644
index 00000000000..661051b3437
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/MavenPluginHandler.java
@@ -0,0 +1,72 @@
+/*
+ * 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.batch.maven;
+
+import org.sonar.api.BatchExtension;
+import org.sonar.api.resources.Project;
+
+/**
+ * @since 1.10
+ */
+public interface MavenPluginHandler extends BatchExtension {
+
+ /**
+ * The plugin group id
+ *
+ * @return the group id
+ */
+ String getGroupId();
+
+ /**
+ * The plugin artifact id
+ *
+ * @return artifact id
+ */
+ String getArtifactId();
+
+ /**
+ * The fixed plugin version to execute
+ *
+ * @return the plugin version
+ */
+ String getVersion();
+
+ /**
+ * Indicates if the plugin version should be fixed or not, it means that if your pom defines another version
+ * than the one defined by getVersion(), this version will be used
+ *
+ * @return true if the version should be fixed
+ */
+ boolean isFixedVersion();
+
+ /**
+ * The maven goals to execute
+ *
+ * @return an array of goals
+ */
+ String[] getGoals();
+
+ /**
+ * Configures the pom being executed, add or remove plugin properties.
+ * This method is automatically executed by Sonar. Plugins do NOT have to execute it.
+ */
+ void configure(Project project, MavenPlugin plugin);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/MavenSurefireUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/MavenSurefireUtils.java
new file mode 100644
index 00000000000..10406c81f6e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/MavenSurefireUtils.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.api.batch.maven;
+
+import org.sonar.api.resources.Project;
+
+/**
+ * @since 1.10
+ */
+public final class MavenSurefireUtils {
+
+ public static final String GROUP_ID = MavenUtils.GROUP_ID_APACHE_MAVEN;
+ public static final String ARTIFACT_ID = "maven-surefire-plugin";
+ public static final String VERSION = "2.4.3";
+
+ private MavenSurefireUtils() {
+ }
+
+ /**
+ * Configures the project POM with base required surefire settings
+ *
+ * @param project the project currently analyzed
+ * @return the configured surefire MavenPlugin object instance, cannot be null
+ */
+ public static MavenPlugin configure(Project project) {
+ MavenPlugin surefire = MavenPlugin.registerPlugin(project.getPom(), GROUP_ID, ARTIFACT_ID, VERSION, false);
+ surefire.setParameter("disableXmlReport", "false");
+ surefire.setParameter("testFailureIgnore", "true");
+ return surefire;
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/MavenUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/MavenUtils.java
new file mode 100644
index 00000000000..58719ab80bb
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/batch/maven/MavenUtils.java
@@ -0,0 +1,140 @@
+/*
+ * 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.batch.maven;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.maven.model.Plugin;
+import org.apache.maven.model.ReportPlugin;
+import org.apache.maven.project.MavenProject;
+import org.slf4j.LoggerFactory;
+
+import java.nio.charset.Charset;
+import java.util.Collection;
+
+/**
+ * An utility class to manipulate Maven concepts
+ *
+ * @since 1.10
+ */
+public final class MavenUtils {
+
+ public static final String GROUP_ID_APACHE_MAVEN = "org.apache.maven.plugins";
+ public static final String GROUP_ID_CODEHAUS_MOJO = "org.codehaus.mojo";
+
+ private MavenUtils() {
+ // utility class with only static methods
+ }
+
+ /**
+ * Returns the version of Java used by the maven compiler plugin
+ *
+ * @param pom the project pom
+ * @return the java version
+ */
+ public static String getJavaVersion(MavenProject pom) {
+ MavenPlugin compilerPlugin = MavenPlugin.getPlugin(pom, GROUP_ID_APACHE_MAVEN, "maven-compiler-plugin");
+ if (compilerPlugin != null) {
+ return compilerPlugin.getParameter("target");
+ }
+ return null;
+ }
+
+ public static String getJavaSourceVersion(MavenProject pom) {
+ MavenPlugin compilerPlugin = MavenPlugin.getPlugin(pom, GROUP_ID_APACHE_MAVEN, "maven-compiler-plugin");
+ if (compilerPlugin != null) {
+ return compilerPlugin.getParameter("source");
+ }
+ return null;
+ }
+
+ /**
+ * Queries a collection of plugins based on a group id and an artifact id and returns the plugin if it exists
+ *
+ * @param plugins the plugins collection
+ * @param groupId the group id
+ * @param artifactId the artifact id
+ * @return the corresponding plugin if it exists, null otherwise
+ */
+ public static Plugin getPlugin(Collection<Plugin> plugins, String groupId, String artifactId) {
+ if (plugins != null) {
+ for (Plugin plugin : plugins) {
+ if (equals(plugin, groupId, artifactId)) {
+ return plugin;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Tests whether a plugin has got a given artifact id and group id
+ *
+ * @param plugin the plugin to test
+ * @param groupId the group id
+ * @param artifactId the artifact id
+ * @return whether the plugin has got group + artifact ids
+ */
+ public static boolean equals(Plugin plugin, String groupId, String artifactId) {
+ if (plugin != null && plugin.getArtifactId().equals(artifactId)) {
+ if (plugin.getGroupId() == null) {
+ return groupId == null || groupId.equals(MavenUtils.GROUP_ID_APACHE_MAVEN) || groupId.equals(MavenUtils.GROUP_ID_CODEHAUS_MOJO);
+ }
+ return plugin.getGroupId().equals(groupId);
+ }
+ return false;
+ }
+
+ /**
+ * Tests whether a ReportPlugin has got a given artifact id and group id
+ *
+ * @param plugin the ReportPlugin to test
+ * @param groupId the group id
+ * @param artifactId the artifact id
+ * @return whether the ReportPlugin has got group + artifact ids
+ */
+ public static boolean equals(ReportPlugin plugin, String groupId, String artifactId) {
+ if (plugin != null && plugin.getArtifactId().equals(artifactId)) {
+ if (plugin.getGroupId() == null) {
+ return groupId == null || groupId.equals(MavenUtils.GROUP_ID_APACHE_MAVEN) || groupId.equals(MavenUtils.GROUP_ID_CODEHAUS_MOJO);
+ }
+ return plugin.getGroupId().equals(groupId);
+ }
+ return false;
+ }
+
+ /**
+ * Returns the charset of a pom
+ *
+ * @param pom the project pom
+ * @return thee charset
+ */
+ public static Charset getSourceCharset(MavenProject pom) {
+ String encoding = pom.getProperties().getProperty("project.build.sourceEncoding");
+ if (StringUtils.isNotEmpty(encoding)) {
+ try {
+ return Charset.forName(encoding);
+
+ } catch (Exception e) {
+ LoggerFactory.getLogger(MavenUtils.class).warn("Can not get project charset", e);
+ }
+ }
+ return Charset.defaultCharset();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/charts/AbstractChart.java b/sonar-plugin-api/src/main/java/org/sonar/api/charts/AbstractChart.java
new file mode 100644
index 00000000000..72c26d3e587
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/charts/AbstractChart.java
@@ -0,0 +1,97 @@
+/*
+ * 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.charts;
+
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.plot.CategoryPlot;
+import org.jfree.chart.plot.Plot;
+import org.jfree.chart.renderer.AbstractRenderer;
+import org.jfree.chart.title.TextTitle;
+import org.jfree.data.Values2D;
+
+import java.awt.*;
+import java.awt.image.BufferedImage;
+
+/**
+ * An extension point to generate JFreeChart charts
+ * @since 1.10
+ */
+public abstract class AbstractChart implements Chart {
+
+ public static final int FONT_SIZE = 13;
+ public static final Color OUTLINE_COLOR = new Color(51, 51, 51);
+ public static final Color GRID_COLOR = new Color(204, 204, 204);
+ public static final Color[] COLORS = new Color[]{Color.decode("#4192D9"), Color.decode("#800000"), Color.decode("#A7B307"), Color.decode("#913C9F"), Color.decode("#329F4D")};
+
+
+ protected abstract Plot getPlot(ChartParameters params);
+
+ protected boolean hasLegend() {
+ return false;
+ }
+
+ /**
+ * Generates a JFreeChart chart using a set of parameters
+ *
+ * @param params the chart parameters
+ * @return the generated chart
+ */
+ public BufferedImage generateImage(ChartParameters params) {
+ JFreeChart chart = new JFreeChart(null, TextTitle.DEFAULT_FONT, getPlot(params), hasLegend());
+ improveChart(chart, params);
+ return chart.createBufferedImage(params.getWidth(), params.getHeight());
+ }
+
+ private void improveChart(JFreeChart jfrechart, ChartParameters params) {
+ Color background = Color.decode("#" + params.getValue(ChartParameters.PARAM_BACKGROUND_COLOR, "FFFFFF", false));
+ jfrechart.setBackgroundPaint(background);
+
+ jfrechart.setBorderVisible(false);
+ jfrechart.setAntiAlias(true);
+ jfrechart.setTextAntiAlias(true);
+ jfrechart.removeLegend();
+ }
+
+ @Override
+ public String toString() {
+ return getKey();
+ }
+
+
+ /**
+ * Helper to set color of series. If the parameter colorsHex is null, then default Sonar colors are used.
+ */
+ protected void configureColors(Values2D dataset, CategoryPlot plot, String[] colorsHex) {
+ Color[] colors = COLORS;
+ if (colorsHex != null && colorsHex.length > 0) {
+ colors = new Color[colorsHex.length];
+ for (int i = 0; i < colorsHex.length; i++) {
+ colors[i] = Color.decode("#" + colorsHex[i]);
+ }
+ }
+
+ dataset.getColumnCount();
+ AbstractRenderer renderer = (AbstractRenderer) plot.getRenderer();
+ for (int i = 0; i < dataset.getColumnCount(); i++) {
+ renderer.setSeriesPaint(i, colors[i % colors.length]);
+
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/charts/Chart.java b/sonar-plugin-api/src/main/java/org/sonar/api/charts/Chart.java
new file mode 100644
index 00000000000..21a661007f0
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/charts/Chart.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.charts;
+
+import org.sonar.api.ServerExtension;
+
+import java.awt.image.BufferedImage;
+
+/**
+ * An Extension to create charts
+ *
+ * @since 1.10
+ */
+public interface Chart extends ServerExtension {
+ String getKey();
+
+ /**
+ * The method to implement to generate the chart
+ *
+ * @param params the chart parameters
+ * @return the image generated
+ */
+ BufferedImage generateImage(ChartParameters params);
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/charts/ChartParameters.java b/sonar-plugin-api/src/main/java/org/sonar/api/charts/ChartParameters.java
new file mode 100644
index 00000000000..7af4babe623
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/charts/ChartParameters.java
@@ -0,0 +1,184 @@
+/*
+ * 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.charts;
+
+import org.apache.commons.lang.CharEncoding;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.text.StrTokenizer;
+import org.sonar.api.utils.SonarException;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * The class to hold parameters to configure a chart
+ * @since 1.10
+ */
+public class ChartParameters {
+ private static final String[] EMPTY = new String[0];
+
+ public static final String PARAM_WIDTH = "w";
+ public static final String PARAM_BACKGROUND_COLOR = "bgc";
+ public static final String PARAM_HEIGHT = "h";
+ public static final int MAX_WIDTH = 700;
+ public static final String PARAM_LOCALE = "locale";
+
+ public static final int MAX_HEIGHT = 500;
+ public static final int DEFAULT_WIDTH = 200;
+
+ public static final int DEFAULT_HEIGHT = 200;
+
+
+ private Map<String, String> params;
+
+ /**
+ * Creates a ChartParameter based on a list of parameters
+ * @param params the list of parameters
+ */
+ public ChartParameters(Map<String, String> params) {
+ this.params = params;
+ }
+
+ /**
+ * Creates a Chartparameter based on a query string with a format key1=value1&key2=value2...
+ *
+ * @param queryString string
+ */
+ public ChartParameters(String queryString) {
+ this.params = new HashMap<String, String>();
+ String[] groups = StringUtils.split(queryString, "&");
+ for (String group : groups) {
+ String[] keyval = StringUtils.split(group, "=");
+ params.put(keyval[0], keyval[1]);
+ }
+ }
+
+ /**
+ * Shortcut to getValue with no decoding and no default value
+ * @param key the param ket
+ * @return the value of the param
+ */
+ public String getValue(String key) {
+ return getValue(key, "", false);
+ }
+
+ /**
+ * Returns the [decoded or not] value of a param from its key or the default value
+ * if id does not exist
+ *
+ * @param key the param ket
+ * @param defaultValue the default value if not exist
+ * @param decode whther the value should be decoded
+ * @return the value of the param
+ */
+
+ public String getValue(String key, String defaultValue, boolean decode) {
+ String val = params.get(key);
+ if (decode) {
+ val = decode(val);
+ }
+ if (val == null) {
+ val = defaultValue;
+ }
+ return val;
+ }
+
+ /**
+ * Returns an array of a param values, given its key and the values delimiter
+ *
+ * @param key the param key
+ * @param delimiter the values delimiter
+ * @return the list of vaalues
+ */
+ public String[] getValues(String key, String delimiter) {
+ String value = params.get(key);
+ if (value != null) {
+ return StringUtils.split(value, delimiter);
+ }
+ return EMPTY;
+ }
+
+ /**
+ * Returns an array of a param values, given its key and the values delimiter
+ * Values can be decoded or not
+ *
+ * @param key the param key
+ * @param delimiter the values delimiter
+ * @param decode whether to decode values
+ * @return the list of vaalues
+ */
+ public String[] getValues(String key, String delimiter, boolean decode) {
+ String value = params.get(key);
+ if (value != null) {
+ if (decode) {
+ value = decode(value);
+ }
+ return new StrTokenizer(value, delimiter).setIgnoreEmptyTokens(false).getTokenArray();
+ }
+ return EMPTY;
+ }
+
+ /**
+ * Get the chart width
+ *
+ * @return width
+ */
+ public int getWidth() {
+ int width = Integer.parseInt(getValue(PARAM_WIDTH, "" + DEFAULT_WIDTH, false));
+ return Math.min(width, MAX_WIDTH);
+ }
+
+ /**
+ * Get the chart height
+ *
+ * @return height
+ */
+ public int getHeight() {
+ int height = Integer.parseInt(getValue(PARAM_HEIGHT, "" + DEFAULT_HEIGHT, false));
+ return Math.min(height, MAX_HEIGHT);
+ }
+
+ /**
+ * Get the Locale
+ *
+ * @return Locale
+ */
+ public Locale getLocale() {
+ String locale = getValue(PARAM_LOCALE);
+ if (StringUtils.isNotBlank(locale)) {
+ return new Locale(locale);
+ }
+ return Locale.ENGLISH;
+ }
+
+ private String decode(String val) {
+ if (val != null) {
+ try {
+ val = URLDecoder.decode(val, CharEncoding.UTF_8);
+ } catch (UnsupportedEncodingException e) {
+ throw new SonarException("Decoding chart parameter : " + val, e);
+ }
+ }
+ return val;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/NoSonarFilter.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/NoSonarFilter.java
new file mode 100644
index 00000000000..d583cae1575
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/NoSonarFilter.java
@@ -0,0 +1,50 @@
+/*
+ * 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.api.resources.Resource;
+import org.sonar.api.rules.Violation;
+import org.sonar.api.rules.ViolationFilter;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @since 2.1
+ */
+public class NoSonarFilter implements ViolationFilter {
+
+ private final Map<Resource, Set<Integer>> noSonarLinesByResource = new HashMap<Resource, Set<Integer>>();
+
+ public void addResource(Resource resource, Set<Integer> noSonarLines) {
+ if (resource != null && noSonarLines != null) {
+ noSonarLinesByResource.put(resource, noSonarLines);
+ }
+ }
+
+ public boolean isIgnored(Violation violation) {
+ if (violation.getResource() != null && violation.getLineId() != null) {
+ Set<Integer> noSonarLines = noSonarLinesByResource.get(violation.getResource());
+ return (noSonarLines != null && noSonarLines.contains(violation.getLineId()));
+ }
+ return false;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/checkers/AnnotationCheckerFactory.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/checkers/AnnotationCheckerFactory.java
new file mode 100644
index 00000000000..b12b8dcc34c
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/checkers/AnnotationCheckerFactory.java
@@ -0,0 +1,157 @@
+/*
+ * 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.apache.commons.lang.StringUtils;
+import org.sonar.api.checks.profiles.Check;
+import org.sonar.api.checks.profiles.CheckProfile;
+import org.sonar.check.AnnotationIntrospector;
+import org.sonar.check.CheckProperty;
+
+import java.lang.reflect.Field;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * @since 2.1
+ */
+public class AnnotationCheckerFactory<CHECKER> extends CheckerFactory<CHECKER> {
+
+ private CheckProfile profile;
+ private String repositoryKey;
+ private Collection<Class<CHECKER>> checkerClasses;
+
+ public AnnotationCheckerFactory(CheckProfile profile, String repositoryKey, Collection<Class<CHECKER>> checkerClasses) {
+ this.profile = profile;
+ this.repositoryKey = repositoryKey;
+ this.checkerClasses = checkerClasses;
+ }
+
+ public Map<Check, CHECKER> create() {
+ Map<String, Class<CHECKER>> classesByKey = getClassesByKey(checkerClasses);
+
+ Map<Check, CHECKER> map = new IdentityHashMap<Check, CHECKER>();
+ for (Check check : profile.getChecks(repositoryKey)) {
+ Class<CHECKER> clazz = classesByKey.get(check.getTemplateKey());
+ if (clazz != null) {
+ CHECKER checker = instantiate(check, clazz);
+ if (checker != null) {
+ map.put(check, checker);
+ }
+ }
+ }
+ return map;
+ }
+
+ CHECKER instantiate(Check check, Class<CHECKER> clazz) {
+ try {
+ CHECKER checker = clazz.newInstance();
+ configureFields(check, checker);
+ return checker;
+
+ } catch (UnvalidCheckerException e) {
+ throw e;
+
+ } catch (Exception e) {
+ throw new UnvalidCheckerException("The checker " + clazz.getCanonicalName() + " can not be created", e);
+ }
+ }
+
+ private void configureFields(Check check, CHECKER checker) throws IllegalAccessException {
+ for (Map.Entry<String, String> entry : check.getProperties().entrySet()) {
+ Field field = getField(checker, entry.getKey());
+ if (field == null) {
+ throw new UnvalidCheckerException("The field " + entry.getKey() + " does not exist or is not annotated with @CheckProperty");
+ }
+ if (StringUtils.isNotBlank(entry.getValue())) {
+ configureField(checker, field, entry);
+ }
+ }
+
+ }
+
+ private void configureField(Object checker, Field field, Map.Entry<String, String> parameter) throws IllegalAccessException {
+ field.setAccessible(true);
+
+ if (field.getType().equals(String.class)) {
+ field.set(checker, parameter.getValue());
+
+ } else if (field.getType().getSimpleName().equals("int")) {
+ field.setInt(checker, Integer.parseInt(parameter.getValue()));
+
+ } else if (field.getType().getSimpleName().equals("short")) {
+ field.setShort(checker, Short.parseShort(parameter.getValue()));
+
+ } else if (field.getType().getSimpleName().equals("long")) {
+ field.setLong(checker, Long.parseLong(parameter.getValue()));
+
+ } else if (field.getType().getSimpleName().equals("double")) {
+ field.setDouble(checker, Double.parseDouble(parameter.getValue()));
+
+ } else if (field.getType().getSimpleName().equals("boolean")) {
+ field.setBoolean(checker, Boolean.parseBoolean(parameter.getValue()));
+
+ } else if (field.getType().getSimpleName().equals("byte")) {
+ field.setByte(checker, Byte.parseByte(parameter.getValue()));
+
+ } else if (field.getType().equals(Integer.class)) {
+ field.set(checker, new Integer(Integer.parseInt(parameter.getValue())));
+
+ } else if (field.getType().equals(Long.class)) {
+ field.set(checker, new Long(Long.parseLong(parameter.getValue())));
+
+ } else if (field.getType().equals(Double.class)) {
+ field.set(checker, new Double(Double.parseDouble(parameter.getValue())));
+
+ } else if (field.getType().equals(Boolean.class)) {
+ field.set(checker, Boolean.valueOf(Boolean.parseBoolean(parameter.getValue())));
+
+ } else {
+ throw new UnvalidCheckerException("The type of the field " + field + " is not supported: " + field.getType());
+ }
+ }
+
+ private Field getField(Object checker, String key) {
+ Field[] fields = checker.getClass().getDeclaredFields();
+ for (Field field : fields) {
+ CheckProperty annotation = field.getAnnotation(CheckProperty.class);
+ if (annotation != null) {
+ if (key.equals(field.getName()) || key.equals(annotation.key())) {
+ return field;
+ }
+ }
+ }
+ return null;
+ }
+
+ private Map<String, Class<CHECKER>> getClassesByKey(Collection<Class<CHECKER>> checkerClasses) {
+ Map<String, Class<CHECKER>> result = new HashMap<String, Class<CHECKER>>();
+ for (Class<CHECKER> checkerClass : checkerClasses) {
+ String key = AnnotationIntrospector.getCheckKey(checkerClass);
+ if (key != null) {
+ result.put(key, checkerClass);
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/checkers/CheckerFactory.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/checkers/CheckerFactory.java
new file mode 100644
index 00000000000..62d46f67de3
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/checkers/CheckerFactory.java
@@ -0,0 +1,31 @@
+/*
+ * 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.api.checks.profiles.Check;
+
+import java.util.Map;
+
+/**
+ * @since 2.1
+ */
+public abstract class CheckerFactory<CHECKER> {
+ public abstract Map<Check, CHECKER> create();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/checkers/MessageDispatcher.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/checkers/MessageDispatcher.java
new file mode 100644
index 00000000000..6321210f2a5
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/checkers/MessageDispatcher.java
@@ -0,0 +1,106 @@
+/*
+ * 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 com.google.common.collect.Maps;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.checks.profiles.Check;
+import org.sonar.api.checks.profiles.CheckProfile;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RulePriority;
+import org.sonar.api.rules.Violation;
+import org.sonar.check.Message;
+
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Map;
+
+public class MessageDispatcher {
+
+ private Map<Check, Object> checkersByCheck;
+ private Map<Object, Check> checksByChecker;
+ private SensorContext context;
+
+ public MessageDispatcher(SensorContext context) {
+ this.context = context;
+ checkersByCheck = Maps.newIdentityHashMap();
+ checksByChecker = Maps.newIdentityHashMap();
+ }
+
+ public void registerChecker(Check check, Object checker) {
+ checkersByCheck.put(check, checker);
+ checksByChecker.put(checker, check);
+ }
+
+ public void registerCheckers(CheckerFactory factory) {
+ Map<Check, Object> map = factory.create();
+ for (Map.Entry<Check, Object> entry : map.entrySet()) {
+ registerChecker(entry.getKey(), entry.getValue());
+ }
+ }
+
+ public void registerCheckers(CheckProfile profile) {
+ for (Check check : profile.getChecks()) {
+ registerChecker(check, check);
+ }
+ }
+
+ public Object getChecker(Check check) {
+ return checkersByCheck.get(check);
+ }
+
+ public Check getCheck(Object checker) {
+ return checksByChecker.get(checker);
+ }
+
+ public Collection getCheckers() {
+ return checkersByCheck.values();
+ }
+
+ public void unregisterCheck(Check check) {
+ Object checker = checkersByCheck.remove(check);
+ if (checker != null) {
+ checksByChecker.remove(checker);
+ }
+ }
+
+ public void unregisterChecks(CheckProfile profile) {
+ for (Check check : profile.getChecks()) {
+ unregisterCheck(check);
+ }
+ }
+
+ public void log(Resource resource, Message message) {
+ Object checker = message.getChecker();
+ Check check = getCheck(checker);
+ Violation violation = new Violation(new Rule(check.getRepositoryKey(), check.getTemplateKey()), resource);
+ violation.setLineId(message.getLine());
+ violation.setMessage(message.getText(Locale.ENGLISH));
+ violation.setPriority(RulePriority.fromCheckPriority(check.getPriority()));
+ context.saveViolation(violation);
+ }
+
+ public void clear() {
+ checkersByCheck.clear();
+ checksByChecker.clear();
+ }
+
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/checkers/UnvalidCheckerException.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/checkers/UnvalidCheckerException.java
new file mode 100644
index 00000000000..e732b77c645
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/checkers/UnvalidCheckerException.java
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+public class UnvalidCheckerException extends RuntimeException {
+ public UnvalidCheckerException() {
+ }
+
+ public UnvalidCheckerException(String message) {
+ super(message);
+ }
+
+ public UnvalidCheckerException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public UnvalidCheckerException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/AnnotationCheckProfileFactory.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/AnnotationCheckProfileFactory.java
new file mode 100644
index 00000000000..056461b3953
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/AnnotationCheckProfileFactory.java
@@ -0,0 +1,67 @@
+/*
+ * 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.profiles;
+
+import org.sonar.check.AnnotationIntrospector;
+import org.sonar.check.BelongsToProfile;
+import org.sonar.check.BelongsToProfiles;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+public final class AnnotationCheckProfileFactory {
+
+ private AnnotationCheckProfileFactory() {
+ }
+
+ public static Collection<CheckProfile> create(String repositoryKey, String language, Collection<Class> checkClasses) {
+ Map<String, CheckProfile> profilesByTitle = new HashMap<String, CheckProfile>();
+
+ if (checkClasses != null) {
+ for (Class aClass : checkClasses) {
+ BelongsToProfiles belongsToProfiles = (BelongsToProfiles) aClass.getAnnotation(BelongsToProfiles.class);
+ if (belongsToProfiles != null) {
+ for (BelongsToProfile belongsToProfile : belongsToProfiles.value()) {
+ registerProfile(profilesByTitle, aClass, belongsToProfile, repositoryKey, language);
+ }
+ }
+ BelongsToProfile belongsToProfile = (BelongsToProfile) aClass.getAnnotation(BelongsToProfile.class);
+ registerProfile(profilesByTitle, aClass, belongsToProfile, repositoryKey, language);
+ }
+ }
+
+ return profilesByTitle.values();
+ }
+
+ private static void registerProfile(Map<String, CheckProfile> profilesByTitle, Class aClass, BelongsToProfile belongsToProfile, String repositoryKey, String language) {
+ if (belongsToProfile != null) {
+ String title = belongsToProfile.title();
+ CheckProfile profile = profilesByTitle.get(title);
+ if (profile == null) {
+ profile = new CheckProfile(title, language);
+ profilesByTitle.put(title, profile);
+ }
+ Check check = new Check(repositoryKey, AnnotationIntrospector.getCheckKey(aClass));
+ check.setPriority(belongsToProfile.priority());
+ profile.addCheck(check);
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/Check.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/Check.java
new file mode 100644
index 00000000000..617a084a695
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/Check.java
@@ -0,0 +1,109 @@
+/*
+ * 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.profiles;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.check.Priority;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * EXPERIMENTAL - will be used in version 2.2
+ */
+public class Check {
+
+ private String repositoryKey;
+ private String templateKey;
+ private Priority priority = null;
+ private final Map<String, String> properties = new HashMap<String,String>();
+
+ public Check() {
+ }
+
+ public Check(String repositoryKey, String templateKey) {
+ this.repositoryKey = repositoryKey;
+ this.templateKey = templateKey;
+ }
+
+ public String getTemplateKey() {
+ return templateKey;
+ }
+
+ public String getRepositoryKey() {
+ return repositoryKey;
+ }
+
+ public void setRepositoryKey(String repositoryKey) {
+ this.repositoryKey = repositoryKey;
+ }
+
+ public void setTemplateKey(String templateKey) {
+ this.templateKey = templateKey;
+ }
+
+ public Priority getPriority() {
+ return priority;
+ }
+
+ public void setPriority(Priority priority) {
+ this.priority = priority;
+ }
+
+ public Map<String, String> getProperties() {
+ return properties;
+ }
+
+ public String getProperty(String key) {
+ return properties.get(key);
+ }
+
+ public void addProperty(String key, Object value) {
+ properties.put(key, value.toString());
+ }
+
+ public void addProperties(Map<String, String> properties) {
+ this.properties.putAll(properties);
+ }
+
+ public void setProperties(Map<String, String> properties) {
+ this.properties.clear();
+ this.properties.putAll(properties);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o == this;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("repository", repositoryKey)
+ .append("template", templateKey)
+ .append("priority", priority)
+ .toString();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/CheckProfile.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/CheckProfile.java
new file mode 100644
index 00000000000..7bb3c560af5
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/CheckProfile.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.profiles;
+
+import org.sonar.api.BatchExtension;
+import org.sonar.api.ServerExtension;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class CheckProfile implements BatchExtension, ServerExtension {
+
+ private String name;
+ private String language;
+ private List<Check> checks = new ArrayList<Check>();
+
+ public CheckProfile(String name, String language) {
+ if (name == null) {
+ throw new IllegalArgumentException("Name can not be null");
+ }
+ if (language == null) {
+ throw new IllegalArgumentException("Language can not be null");
+ }
+ this.name = name;
+ this.language = language;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public List<Check> getChecks() {
+ return checks;
+ }
+
+ public List<Check> getChecks(String repositoryKey) {
+ List<Check> result = new ArrayList<Check>();
+ for (Check check : getChecks()) {
+ if (check.getRepositoryKey().equals(repositoryKey)) {
+ result.add(check);
+ }
+ }
+ return result;
+ }
+
+ public List<Check> getChecks(String repositoryKey, String templateKey) {
+ List<Check> result = new ArrayList<Check>();
+ List<Check> repoChecks = getChecks(repositoryKey);
+ for (Check repoCheck : repoChecks) {
+ if (repoCheck.getTemplateKey().equals(templateKey)) {
+ result.add(repoCheck);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * We assume there is only one check for this template
+ */
+ public Check getCheck(String repositoryKey, String templateKey) {
+ List<Check> repoChecks = getChecks(repositoryKey);
+ for (Check repoCheck : repoChecks) {
+ if (repoCheck.getTemplateKey().equals(templateKey)) {
+ return repoCheck;
+ }
+ }
+ return null;
+ }
+
+ public void addCheck(Check check) {
+ checks.add(check);
+ }
+
+ public void setChecks(Collection<Check> list) {
+ checks.clear();
+ checks.addAll(list);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ CheckProfile profile = (CheckProfile) o;
+ if (!language.equals(profile.language)) {
+ return false;
+ }
+ if (!name.equals(profile.name)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = name.hashCode();
+ result = 31 * result + language.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/CheckProfileProvider.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/CheckProfileProvider.java
new file mode 100644
index 00000000000..8eb21ef01da
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/CheckProfileProvider.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.profiles;
+
+import org.sonar.api.ServerExtension;
+
+import java.util.Collection;
+
+/**
+ * EXPERIMENTAL - will be used in version 2.2
+ */
+public abstract class CheckProfileProvider implements ServerExtension {
+
+ public abstract Collection<CheckProfile> provide();
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/CheckProfileXmlMarshaller.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/CheckProfileXmlMarshaller.java
new file mode 100644
index 00000000000..3203b14e1e3
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/profiles/CheckProfileXmlMarshaller.java
@@ -0,0 +1,154 @@
+/*
+ * 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.profiles;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.converters.Converter;
+import com.thoughtworks.xstream.converters.MarshallingContext;
+import com.thoughtworks.xstream.converters.UnmarshallingContext;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+import com.thoughtworks.xstream.io.xml.CompactWriter;
+import com.thoughtworks.xstream.io.xml.XppDriver;
+import org.apache.commons.io.IOUtils;
+import org.sonar.check.Priority;
+
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Map;
+
+/**
+ * EXPERIMENTAL - will be used in version 2.3
+ */
+public final class CheckProfileXmlMarshaller {
+
+ public static void toXml(CheckProfile profile, Writer writer) {
+ getXStream().toXML(profile, writer);
+ }
+
+ public static CheckProfile fromXml(Reader xml) {
+ return (CheckProfile) getXStream().fromXML(xml);
+ }
+
+ public static CheckProfile fromXmlInClasspath(String pathToXml) {
+ return fromXmlInClasspath(pathToXml, CheckProfileXmlMarshaller.class);
+ }
+
+ public static CheckProfile fromXmlInClasspath(String pathToXml, Class clazz) {
+ Reader reader = new InputStreamReader(clazz.getResourceAsStream(pathToXml));
+ try {
+ return fromXml(reader);
+ } finally {
+ IOUtils.closeQuietly(reader);
+ }
+ }
+
+ private static XStream getXStream() {
+ XStream xstream = new XStream(new CompactDriver());
+ xstream.setClassLoader(CheckProfileXmlMarshaller.class.getClassLoader());
+ xstream.registerConverter(new CheckConverter());
+ xstream.alias("profile", CheckProfile.class);
+ xstream.alias("check", Check.class);
+ xstream.addImplicitCollection(CheckProfile.class, "checks");
+ return xstream;
+ }
+
+ private static class CompactDriver extends XppDriver {
+ @Override
+ public HierarchicalStreamWriter createWriter(Writer out) {
+ return new XDataPrintWriter(out);
+ }
+ }
+
+
+ private static class XDataPrintWriter extends CompactWriter {
+ public XDataPrintWriter(Writer writer) {
+ super(writer, XML_1_0);
+ }
+ }
+
+ private static class CheckConverter implements Converter {
+
+ public boolean canConvert(Class clazz) {
+ return clazz.equals(Check.class);
+ }
+
+ public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
+ Check check = (Check) value;
+ writer.startNode("repository");
+ writer.setValue(check.getRepositoryKey());
+ writer.endNode();
+ writer.startNode("template");
+ writer.setValue(check.getTemplateKey());
+ writer.endNode();
+ writer.startNode("priority");
+ writer.setValue(check.getPriority().toString());
+ writer.endNode();
+ for (Map.Entry<String, String> entry : check.getProperties().entrySet()) {
+ if (entry.getValue() != null) {
+ writer.startNode("property");
+ writer.startNode("key");
+ writer.setValue(entry.getKey());
+ writer.endNode();
+ writer.startNode("value");
+ // TODO is escaping automatically supported by xstream ?
+ writer.setValue(entry.getValue());
+ writer.endNode();
+ writer.endNode();
+ }
+ }
+ }
+
+ public Object unmarshal(HierarchicalStreamReader reader,
+ UnmarshallingContext context) {
+ Check check = new Check();
+ while (reader.hasMoreChildren()) {
+ reader.moveDown();
+ readValue(reader, check);
+ reader.moveUp();
+ }
+ return check;
+ }
+
+ private void readValue(HierarchicalStreamReader reader, Check check) {
+ if (reader.getNodeName().equals("repository")) {
+ check.setRepositoryKey(reader.getValue());
+
+ } else if (reader.getNodeName().equals("template")) {
+ check.setTemplateKey(reader.getValue());
+
+ } else if (reader.getNodeName().equals("priority")) {
+ check.setPriority(Priority.valueOf(reader.getValue()));
+
+ } else if (reader.getNodeName().equals("property")) {
+ reader.moveDown();
+ String key = reader.getValue();
+ reader.moveUp();
+
+ reader.moveDown();
+ String value = reader.getValue();
+ reader.moveUp();
+ check.addProperty(key, value);
+ }
+ }
+
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/AnnotationCheckTemplateFactory.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/AnnotationCheckTemplateFactory.java
new file mode 100644
index 00000000000..7ea9638c8e7
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/AnnotationCheckTemplateFactory.java
@@ -0,0 +1,112 @@
+/*
+ * 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.templates;
+
+import org.apache.commons.lang.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.check.AnnotationIntrospector;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Load templates from class annotations (see the library sonar-check-api)
+ */
+public class AnnotationCheckTemplateFactory {
+
+ private static final Logger LOG = LoggerFactory.getLogger(AnnotationCheckTemplateFactory.class);
+
+ private Collection<Class> annotatedClasses;
+
+ public AnnotationCheckTemplateFactory(Collection<Class> annotatedClasses) {
+ this.annotatedClasses = annotatedClasses;
+ }
+
+ public List<CheckTemplate> create() {
+ List<CheckTemplate> templates = new ArrayList<CheckTemplate>();
+ for (Class annotatedClass : annotatedClasses) {
+ BundleCheckTemplate template = create(annotatedClass);
+ if (template != null) {
+ templates.add(template);
+ }
+ }
+ return templates;
+ }
+
+
+ protected BundleCheckTemplate create(Class annotatedClass) {
+ org.sonar.check.Check checkAnnotation = AnnotationIntrospector.getCheckAnnotation(annotatedClass);
+ if (checkAnnotation == null) {
+ LOG.warn("The class " + annotatedClass.getCanonicalName() + " is not a check template. It should be annotated with " + CheckTemplate.class);
+ return null;
+ }
+
+ BundleCheckTemplate check = toTemplate(annotatedClass, checkAnnotation);
+ Field[] fields = annotatedClass.getDeclaredFields();
+ if (fields != null) {
+ for (Field field : fields) {
+ BundleCheckTemplateProperty property = toProperty(check, field);
+ if (property != null) {
+ check.addProperty(property);
+ }
+ }
+ }
+ return check;
+ }
+
+ private static BundleCheckTemplate toTemplate(Class annotatedClass, org.sonar.check.Check checkAnnotation) {
+ String key = AnnotationIntrospector.getCheckKey(annotatedClass);
+ String bundle = getBundleBaseName(checkAnnotation, annotatedClass);
+
+ BundleCheckTemplate check = new BundleCheckTemplate(key, bundle);
+ check.setDefaultDescription(checkAnnotation.description());
+ check.setDefaultTitle(checkAnnotation.title());
+ check.setIsoCategory(checkAnnotation.isoCategory());
+ check.setPriority(checkAnnotation.priority());
+
+ return check;
+ }
+
+ private static String getBundleBaseName(org.sonar.check.Check checkAnnotation, Class annotatedClass) {
+ String bundle = checkAnnotation.bundle();
+ if (StringUtils.isBlank(bundle)) {
+ bundle = annotatedClass.getCanonicalName();
+ }
+ return bundle;
+ }
+
+ private static BundleCheckTemplateProperty toProperty(BundleCheckTemplate check, Field field) {
+ org.sonar.check.CheckProperty propertyAnnotation = field.getAnnotation(org.sonar.check.CheckProperty.class);
+ if (propertyAnnotation != null) {
+ String fieldKey = propertyAnnotation.key();
+ if (fieldKey==null || "".equals(fieldKey)) {
+ fieldKey = field.getName();
+ }
+ BundleCheckTemplateProperty property = new BundleCheckTemplateProperty(check, fieldKey);
+ property.setDefaultTitle(propertyAnnotation.title());
+ property.setDefaultDescription(propertyAnnotation.description());
+ return property;
+ }
+ return null;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/BundleCheckTemplate.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/BundleCheckTemplate.java
new file mode 100644
index 00000000000..c1b50cc1676
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/BundleCheckTemplate.java
@@ -0,0 +1,110 @@
+/*
+ * 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.templates;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Internationalized check template. Translations are loaded from resource bundles (properties files in the classpath)
+ *
+ * @since 2.1
+ */
+public class BundleCheckTemplate extends CheckTemplate {
+ private static final Logger LOG = LoggerFactory.getLogger(BundleCheckTemplate.class);
+
+ private String bundleBaseName;
+ private String defaultTitle;
+ private String defaultDescription;
+
+ protected BundleCheckTemplate(String key, String bundleBaseName) {
+ super(key);
+ this.bundleBaseName = bundleBaseName;
+ }
+
+ protected BundleCheckTemplate(String key, Class bundleClass) {
+ this(key, bundleClass.getCanonicalName());
+ }
+
+ protected String getDefaultTitle() {
+ if (defaultTitle == null || "".equals(defaultTitle)) {
+ return getKey();
+ }
+ return defaultTitle;
+ }
+
+ protected void setDefaultTitle(String defaultTitle) {
+ this.defaultTitle = defaultTitle;
+ }
+
+ protected String getDefaultDescription() {
+ return defaultDescription;
+ }
+
+ protected void setDefaultDescription(String defaultDescription) {
+ this.defaultDescription = defaultDescription;
+ }
+
+ @Override
+ public String getTitle(Locale locale) {
+ return getText("title", locale, getDefaultTitle());
+ }
+
+ @Override
+ public String getDescription(Locale locale) {
+ return getText("description", locale, getDefaultDescription());
+ }
+
+ @Override
+ public String getMessage(Locale locale, String key, Object... params) {
+ return null;
+ }
+
+ protected String getText(String key, Locale locale, String defaultValue) {
+ String result = null;
+ ResourceBundle bundle = getBundle(locale);
+ if (bundle != null) {
+ try {
+ result = bundle.getString(key);
+ } catch (MissingResourceException e) {
+ LOG.debug(e.getMessage());
+ }
+ }
+ if (result == null) {
+ result = defaultValue;
+ }
+ return result;
+ }
+
+ protected ResourceBundle getBundle(Locale locale) {
+ try {
+ if (locale != null) {
+ return ResourceBundle.getBundle(bundleBaseName, locale);
+ }
+ } catch (MissingResourceException e) {
+ // do nothing : use the default values
+ }
+ return null;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/BundleCheckTemplateProperty.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/BundleCheckTemplateProperty.java
new file mode 100644
index 00000000000..10dd2cbfefc
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/BundleCheckTemplateProperty.java
@@ -0,0 +1,64 @@
+/*
+ * 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.templates;
+
+import java.util.Locale;
+
+public class BundleCheckTemplateProperty extends CheckTemplateProperty {
+
+ private BundleCheckTemplate check;
+ private String defaultTitle;
+ private String defaultDescription;
+
+ public BundleCheckTemplateProperty(BundleCheckTemplate check, String key) {
+ setKey(key);
+ this.check = check;
+ }
+
+ public String getDefaultTitle() {
+ if (defaultTitle == null || "".equals(defaultTitle)) {
+ return getKey();
+ }
+ return defaultTitle;
+ }
+
+ public void setDefaultTitle(String s) {
+ this.defaultTitle = s;
+ }
+
+
+ @Override
+ public String getTitle(Locale locale) {
+ return check.getText("property." + getKey() + ".title", locale, getDefaultTitle());
+ }
+
+ public String getDefaultDescription() {
+ return defaultDescription;
+ }
+
+ public void setDefaultDescription(String s) {
+ this.defaultDescription = s;
+ }
+
+ @Override
+ public String getDescription(Locale locale) {
+ return check.getText("property." + getKey() + ".description", locale, getDefaultDescription());
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplate.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplate.java
new file mode 100644
index 00000000000..f2dc214ce56
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplate.java
@@ -0,0 +1,134 @@
+/*
+ * 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.templates;
+
+import org.sonar.check.IsoCategory;
+import org.sonar.check.Priority;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * EXPERIMENTAL - will be used in version 2.2
+ *
+ * @since 2.1
+ */
+public abstract class CheckTemplate {
+
+ protected String key;
+ protected String configKey;
+ protected Priority priority;
+ protected IsoCategory isoCategory;
+ protected List<CheckTemplateProperty> properties;
+
+ public CheckTemplate(String key) {
+ this.key = key;
+ }
+
+ public CheckTemplate() {
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String s) {
+ this.key = s;
+ }
+
+ public Priority getPriority() {
+ return priority;
+ }
+
+ public void setPriority(Priority p) {
+ this.priority = p;
+ }
+
+ public IsoCategory getIsoCategory() {
+ return isoCategory;
+ }
+
+ public void setIsoCategory(IsoCategory c) {
+ this.isoCategory = c;
+ }
+
+ public String getConfigKey() {
+ return configKey;
+ }
+
+ public void setConfigKey(String configKey) {
+ this.configKey = configKey;
+ }
+
+ public abstract String getTitle(Locale locale);
+
+ public abstract String getDescription(Locale locale);
+
+ public abstract String getMessage(Locale locale, String key, Object... params);
+
+ public List<CheckTemplateProperty> getProperties() {
+ if (properties==null) {
+ return Collections.emptyList();
+ }
+ return properties;
+ }
+
+ public void addProperty(CheckTemplateProperty p) {
+ if (properties==null) {
+ properties = new ArrayList<CheckTemplateProperty>();
+ }
+ properties.add(p);
+ }
+
+ public CheckTemplateProperty getProperty(String key) {
+ if (properties!=null) {
+ for (CheckTemplateProperty property : properties) {
+ if (property.getKey().equals(key)) {
+ return property;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Checks are equal within the same plugin. Two plugins can have two different checks with the same key.
+ */
+ @Override
+ public final boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof CheckTemplate)) {
+ return false;
+ }
+
+ CheckTemplate checkTemplate = (CheckTemplate) o;
+ return key.equals(checkTemplate.key);
+ }
+
+ @Override
+ public final int hashCode() {
+ return key.hashCode();
+ }
+
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplateFactory.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplateFactory.java
new file mode 100644
index 00000000000..c1e93e87337
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplateFactory.java
@@ -0,0 +1,32 @@
+/*
+ * 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.templates;
+
+import java.util.Collection;
+
+/**
+ * EXPERIMENTAL - will be used in version 2.2
+ * @since 2.1
+ */
+public abstract class CheckTemplateFactory {
+
+ public abstract Collection<CheckTemplate> create();
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplateProperty.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplateProperty.java
new file mode 100644
index 00000000000..91dc4428cde
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplateProperty.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.templates;
+
+import java.util.Locale;
+
+/**
+ * EXPERIMENTAL - will be used in version 2.2
+ *
+ * @since 2.1
+ */
+public abstract class CheckTemplateProperty implements Comparable<CheckTemplateProperty> {
+
+ protected String key;
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String s) {
+ this.key = s;
+ }
+
+ public abstract String getTitle(Locale locale);
+
+ public String getDescription() {
+ return getDescription(Locale.ENGLISH);
+ }
+
+
+ public abstract String getDescription(Locale locale);
+
+ @Override
+ public final boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof CheckTemplateProperty)) {
+ return false;
+ }
+
+ CheckTemplateProperty that = (CheckTemplateProperty) o;
+ return key.equals(that.key);
+ }
+
+ @Override
+ public final int hashCode() {
+ return key.hashCode();
+ }
+
+ public int compareTo(CheckTemplateProperty o) {
+ return getKey().compareTo(o.getKey());
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplateRepositories.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplateRepositories.java
new file mode 100644
index 00000000000..c5e6ea6395a
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplateRepositories.java
@@ -0,0 +1,62 @@
+/*
+ * 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.templates;
+
+import org.sonar.api.ServerExtension;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @since 2.1
+ */
+public class CheckTemplateRepositories implements ServerExtension {
+
+ private Map<String, CheckTemplateRepository> repositoriesByKey = new HashMap<String, CheckTemplateRepository>();
+
+ public CheckTemplateRepositories(CheckTemplateRepository[] repositories) {
+ if (repositories != null) {
+ for (CheckTemplateRepository templateRepository : repositories) {
+ repositoriesByKey.put(templateRepository.getKey(), templateRepository);
+ }
+ }
+ }
+
+ public CheckTemplateRepositories() {
+ // DO NOT REMOVE THIS CONSTRUCTOR. It is used by Picocontainer when no repositories are available.
+ }
+
+ public CheckTemplateRepository getRepository(String key) {
+ return repositoriesByKey.get(key);
+ }
+
+ public Collection<CheckTemplateRepository> getRepositories() {
+ return repositoriesByKey.values();
+ }
+
+ public CheckTemplate getTemplate(String repositoryKey, String templateKey) {
+ CheckTemplateRepository repo = getRepository(repositoryKey);
+ if (repo != null) {
+ return repo.getTemplate(templateKey);
+ }
+ return null;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplateRepository.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplateRepository.java
new file mode 100644
index 00000000000..4b98660ae44
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/CheckTemplateRepository.java
@@ -0,0 +1,203 @@
+/*
+ * 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.templates;
+
+import org.apache.commons.io.IOUtils;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.resources.Language;
+import org.sonar.api.rules.*;
+import org.sonar.check.IsoCategory;
+
+import java.io.InputStream;
+import java.util.*;
+
+/**
+ * EXPERIMENTAL - will be used in version 2.3
+ *
+ * @since 2.1
+ */
+public class CheckTemplateRepository implements RulesRepository {
+
+ private String key;
+ private Language language;
+ private List<CheckTemplate> templates;
+ private Map<String, CheckTemplate> templatesByKey;
+
+
+ public CheckTemplateRepository() {
+ }
+
+ public CheckTemplateRepository(String key) {
+ if (key == null) {
+ throw new IllegalArgumentException("Key can not be null");
+ }
+ this.key = key;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public CheckTemplateRepository setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public Language getLanguage() {
+ return language;
+ }
+
+ public CheckTemplateRepository setLanguage(Language l) {
+ this.language = l;
+ return this;
+ }
+
+ public List<CheckTemplate> getTemplates() {
+ if (templates == null) {
+ return Collections.emptyList();
+ }
+ return templates;
+ }
+
+ public CheckTemplateRepository setTemplates(List<CheckTemplate> c) {
+ this.templates = c;
+ return this;
+ }
+
+ public CheckTemplate getTemplate(String key) {
+ if (templatesByKey == null || templatesByKey.isEmpty()) {
+ templatesByKey = new HashMap<String, CheckTemplate>();
+ for (CheckTemplate template : templates) {
+ templatesByKey.put(template.getKey(), template);
+ }
+ }
+ return templatesByKey.get(key);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ CheckTemplateRepository that = (CheckTemplateRepository) o;
+ return key.equals(that.key);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return key.hashCode();
+ }
+
+
+ public static CheckTemplateRepository createFromXml(String repositoryKey, Language language, String pathToXml) {
+ InputStream input = CheckTemplateRepository.class.getResourceAsStream(pathToXml);
+ try {
+ List<CheckTemplate> templates = new XmlCheckTemplateFactory().parse(input);
+ CheckTemplateRepository repository = new CheckTemplateRepository(repositoryKey);
+ repository.setTemplates(templates);
+ repository.setLanguage(language);
+ return repository;
+
+ } finally {
+ IOUtils.closeQuietly(input);
+ }
+ }
+
+ public static CheckTemplateRepository createFromAnnotatedClasses(String repositoryKey, Language language, Collection<Class> classes) {
+ AnnotationCheckTemplateFactory factory = new AnnotationCheckTemplateFactory(classes);
+ CheckTemplateRepository repository = new CheckTemplateRepository(repositoryKey);
+ repository.setTemplates(factory.create());
+ repository.setLanguage(language);
+ return repository;
+ }
+
+
+
+
+
+
+
+
+
+
+ /*
+
+ CODE FOR BACKWARD COMPATIBLITY
+ This class should not extend RulesRepository in next versions
+
+ */
+
+
+ public List<Rule> getInitialReferential() {
+ List<Rule> rules = new ArrayList<Rule>();
+ for (CheckTemplate checkTemplate : getTemplates()) {
+ rules.add(toRule(checkTemplate));
+ }
+ return rules;
+ }
+
+ private Rule toRule(CheckTemplate checkTemplate) {
+ Rule rule = new Rule(getKey(), checkTemplate.getKey());
+ rule.setDescription(checkTemplate.getDescription(Locale.ENGLISH));
+ rule.setName(checkTemplate.getTitle(Locale.ENGLISH));
+ rule.setPriority(RulePriority.fromCheckPriority(checkTemplate.getPriority()));
+ rule.setRulesCategory(toRuleCategory(checkTemplate.getIsoCategory()));
+ for (CheckTemplateProperty checkTemplateProperty : checkTemplate.getProperties()) {
+ RuleParam param = rule.createParameter(checkTemplateProperty.getKey());
+ param.setDescription(checkTemplateProperty.getDescription(Locale.ENGLISH));
+ param.setType("s");
+ }
+
+ return rule;
+ }
+
+ private RulesCategory toRuleCategory(IsoCategory isoCategory) {
+ if (isoCategory == IsoCategory.Reliability) {
+ return Iso9126RulesCategories.RELIABILITY;
+ }
+ if (isoCategory == IsoCategory.Efficiency) {
+ return Iso9126RulesCategories.EFFICIENCY;
+ }
+ if (isoCategory == IsoCategory.Maintainability) {
+ return Iso9126RulesCategories.MAINTAINABILITY;
+ }
+ if (isoCategory == IsoCategory.Portability) {
+ return Iso9126RulesCategories.PORTABILITY;
+ }
+ if (isoCategory == IsoCategory.Usability) {
+ return Iso9126RulesCategories.USABILITY;
+ }
+ return null;
+ }
+
+
+ public List<Rule> parseReferential(String fileContent) {
+ return Collections.emptyList();
+ }
+
+ public List<RulesProfile> getProvidedProfiles() {
+ return Collections.emptyList();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/DefaultCheckTemplate.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/DefaultCheckTemplate.java
new file mode 100644
index 00000000000..76f6fb6ca4e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/DefaultCheckTemplate.java
@@ -0,0 +1,81 @@
+/*
+ * 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.templates;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+import java.util.Locale;
+
+/**
+ * EXPERIMENTAL - will be used in version 2.2
+ *
+ * Non-internationalized check
+ *
+ * @since 2.1
+ */
+public class DefaultCheckTemplate extends CheckTemplate {
+
+ private String title;
+ private String description;
+
+ public DefaultCheckTemplate() {
+ }
+
+ public DefaultCheckTemplate(String key) {
+ super(key);
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ @Override
+ public String getTitle(Locale locale) {
+ if (title == null || "".equals(title)) {
+ return getKey();
+ }
+ return title;
+ }
+
+ @Override
+ public String getDescription(Locale locale) {
+ return description;
+ }
+
+ @Override
+ public String getMessage(Locale locale, String key, Object... params) {
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("key", key)
+ .append("title", title)
+ .append("configKey", configKey)
+ .append("priority", priority)
+ .append("isoCategory", isoCategory)
+ .toString();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/DefaultCheckTemplateProperty.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/DefaultCheckTemplateProperty.java
new file mode 100644
index 00000000000..142942b8ced
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/DefaultCheckTemplateProperty.java
@@ -0,0 +1,62 @@
+/*
+ * 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.templates;
+
+import org.sonar.api.checks.templates.CheckTemplateProperty;
+
+import java.util.Locale;
+
+/**
+ * @since 2.1
+ */
+public class DefaultCheckTemplateProperty extends CheckTemplateProperty {
+
+ private String title;
+ private String description;
+
+ public String getTitle() {
+ if (title == null || "".equals(title)) {
+ return getKey();
+ }
+ return title;
+ }
+
+ @Override
+ public String getTitle(Locale locale) {
+ return getTitle();
+ }
+
+ public void setTitle(String s) {
+ this.title = s;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String s) {
+ this.description = s;
+ }
+
+ @Override
+ public String getDescription(Locale locale) {
+ return getDescription();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/XmlCheckTemplateFactory.java b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/XmlCheckTemplateFactory.java
new file mode 100644
index 00000000000..0b51222c53d
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/checks/templates/XmlCheckTemplateFactory.java
@@ -0,0 +1,100 @@
+/*
+ * 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.templates;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.CharEncoding;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RuleParam;
+import org.sonar.api.rules.StandardRulesXmlParser;
+import org.sonar.api.utils.SonarException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * EXPERIMENTAL - will be used in version 2.2
+ * @since 2.1
+ */
+public class XmlCheckTemplateFactory {
+
+ public List<CheckTemplate> parseXml(String xml) {
+ InputStream input = null;
+ try {
+ input = IOUtils.toInputStream(xml, CharEncoding.UTF_8);
+ return parse(input);
+
+ } catch (IOException e) {
+ throw new SonarException("Can't parse xml file", e);
+
+ } finally {
+ IOUtils.closeQuietly(input);
+ }
+ }
+
+ public List<CheckTemplate> parse(Reader reader) {
+ StandardRulesXmlParser parser = new StandardRulesXmlParser();
+ List<Rule> rules = parser.parse(reader);
+ return toCheckTemplates(rules);
+
+ }
+
+ public List<CheckTemplate> parse(InputStream input) {
+ StandardRulesXmlParser parser = new StandardRulesXmlParser();
+ List<Rule> rules = parser.parse(input);
+ return toCheckTemplates(rules);
+
+ }
+
+ private List<CheckTemplate> toCheckTemplates(List<Rule> rules) {
+ List<CheckTemplate> templates = new ArrayList<CheckTemplate>();
+ if (rules != null) {
+ for (Rule rule : rules) {
+ DefaultCheckTemplate template = new DefaultCheckTemplate(rule.getKey());
+ templates.add(template);
+
+ template.setConfigKey(rule.getConfigKey());
+ template.setDescription(rule.getDescription());
+ template.setIsoCategory(rule.getRulesCategory().toIsoCategory());
+ template.setPriority(rule.getPriority().toCheckPriority());
+ template.setTitle(rule.getName());
+
+ if (rule.getParams() != null) {
+ for (RuleParam param : rule.getParams()) {
+ template.addProperty(toProperty(param));
+ }
+ }
+ }
+ }
+ return templates;
+ }
+
+ private CheckTemplateProperty toProperty(RuleParam param) {
+ DefaultCheckTemplateProperty property = new DefaultCheckTemplateProperty();
+ property.setKey(param.getKey());
+ property.setTitle(param.getKey());
+ property.setDescription(param.getDescription());
+ return property;
+ }
+
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/BaseIdentifiable.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/BaseIdentifiable.java
new file mode 100644
index 00000000000..4e4fb0cf122
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/database/BaseIdentifiable.java
@@ -0,0 +1,42 @@
+/*
+ * 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.database;
+
+import javax.persistence.Column;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+
+@MappedSuperclass
+public class BaseIdentifiable {
+
+ @Id
+ @Column(name = "id")
+ @GeneratedValue
+ private Integer id;
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/DatabaseProperties.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/DatabaseProperties.java
new file mode 100644
index 00000000000..603a913cb29
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/database/DatabaseProperties.java
@@ -0,0 +1,55 @@
+/*
+ * 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.database;
+
+public interface DatabaseProperties {
+
+ int MAX_TEXT_SIZE = 16777215;
+
+ String PROP_ISOLATION = "sonar.jdbc.defaultTransactionIsolation";
+ String PROP_URL = "sonar.jdbc.url";
+ String PROP_URL_DEFAULT_VALUE = "jdbc:derby://localhost:1527/sonar";
+ String PROP_DRIVER = "sonar.jdbc.driverClassName";
+ String PROP_DRIVER_DEFAULT_VALUE = "org.apache.derby.jdbc.ClientDriver";
+ String PROP_DRIVER_DEPRECATED = "sonar.jdbc.driver";
+ String PROP_USER = "sonar.jdbc.username";
+ String PROP_USER_DEPRECATED = "sonar.jdbc.user";
+ String PROP_USER_DEFAULT_VALUE = "sonar";
+ String PROP_PASSWORD = "sonar.jdbc.password";
+ String PROP_PASSWORD_DEFAULT_VALUE = "sonar";
+ String PROP_HIBERNATE_HBM2DLL = "sonar.jdbc.hibernate.hbm2ddl";
+ String PROP_HIBERNATE_GENERATE_STATISTICS = "sonar.jdbc.hibernate.generate_statistics";
+ String PROP_DIALECT = "sonar.jdbc.dialect";
+ String PROP_DIALECT_CLASS = "sonar.jdbc.dialectClass";
+ String PROP_JNDI_NAME = "sonar.jdbc.jndiName";
+ String PROP_EMBEDDED_PORT = "sonar.embeddedDatabase.port";
+ String PROP_HIBERNATE_DEFAULT_SCHEMA = "sonar.hibernate.default_schema";
+
+
+ String PROP_EMBEDDED_DATA_DIR = "sonar.embeddedDatabase.dataDir";
+ String DIALECT_DERBY = "derby";
+ String DIALECT_HSQLDB = "hsqldb";
+ String DIALECT_MYSQL = "mysql";
+ String DIALECT_POSTGRESQL = "postgresql";
+ String DIALECT_ORACLE = "oracle";
+ String DIALECT_DB2 = "db2";
+
+ String DIALECT_MSSQL = "mssql";
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/DatabaseSession.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/DatabaseSession.java
new file mode 100644
index 00000000000..ae6d727c00d
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/database/DatabaseSession.java
@@ -0,0 +1,78 @@
+/*
+ * 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.database;
+
+import org.sonar.api.BatchComponent;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import java.util.List;
+
+/**
+ * This component should not accessible from plugin API
+ *
+ * @since 1.10
+ */
+public abstract class DatabaseSession implements BatchComponent {
+
+
+ // IMPORTANT : this value must be the same than the property
+ // hibernate.jdbc.batch_size from /META-INF/persistence.xml (module sonar-database)
+ public static final int BATCH_SIZE = 30;
+
+
+ public abstract EntityManager getEntityManager();
+
+ public abstract void start();
+
+ public abstract void stop();
+
+ public abstract void commit();
+
+ public abstract void rollback();
+
+ public abstract <T> T save(T entity);
+
+ public abstract Object saveWithoutFlush(Object entity);
+
+ public abstract boolean contains(Object entity);
+
+ public abstract void save(Object... entities);
+
+ public abstract Object merge(Object entity);
+
+ public abstract void remove(Object entity);
+
+ public abstract void removeWithoutFlush(Object entity);
+
+ public abstract <T> T reattach(Class<T> entityClass, Object primaryKey);
+
+ public abstract Query createQuery(String hql);
+
+ public abstract <T> T getSingleResult(Query query, T defaultValue);
+
+ public abstract <T> T getEntity(Class<T> entityClass, Object id);
+
+ public abstract <T> T getSingleResult(Class<T> entityClass, Object... criterias);
+
+ public abstract <T> List<T> getResults(Class<T> entityClass, Object... criterias);
+
+ public abstract <T> List<T> getResults(Class<T> entityClass);
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/model/AsyncMeasureSnapshot.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/AsyncMeasureSnapshot.java
new file mode 100644
index 00000000000..c1424846765
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/AsyncMeasureSnapshot.java
@@ -0,0 +1,172 @@
+/*
+ * 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.database.model;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.database.BaseIdentifiable;
+
+import java.util.Date;
+import javax.persistence.*;
+
+/**
+ * Class to map an aysync measure with hibernate model
+ */
+@Entity
+@Table(name = "async_measure_snapshots")
+public class AsyncMeasureSnapshot extends BaseIdentifiable {
+
+ @Column(name = "project_measure_id", updatable = true, nullable = true)
+ private Long measureId;
+
+ @Temporal(TemporalType.TIMESTAMP)
+ @Column(name = "measure_date", updatable = true, nullable = true)
+ private Date measureDate;
+
+ @Column(name = "snapshot_id", updatable = true, nullable = true)
+ private Integer snapshotId;
+
+ @Temporal(TemporalType.TIMESTAMP)
+ @Column(name = "snapshot_date", updatable = true, nullable = true)
+ private Date snapshotDate;
+
+ @Column(name = "metric_id", updatable = true, nullable = true)
+ private Integer metricId;
+
+ @Column(name = "project_id", updatable = true, nullable = true)
+ private Integer projectId;
+
+ /**
+ * This is the constructor to use
+ *
+ * @param measureId
+ * @param snapshotId the snapshot id to which the measure is attached
+ * @param measureDate the date of the measure
+ * @param snapshotDate the snapshot date
+ * @param metricId the metric the measure is attached to
+ * @param projectId the id of the project
+ */
+ public AsyncMeasureSnapshot(Long measureId, Integer snapshotId, Date measureDate, Date snapshotDate, Integer metricId, Integer projectId) {
+ this.measureId = measureId;
+ this.measureDate = measureDate;
+ this.snapshotId = snapshotId;
+ this.snapshotDate = snapshotDate;
+ this.projectId = projectId;
+ this.metricId = metricId;
+ }
+
+ /**
+ * Default constructor
+ */
+ public AsyncMeasureSnapshot() {
+ }
+
+ public Long getMeasureId() {
+ return measureId;
+ }
+
+ public void setMeasureId(Long measureId) {
+ this.measureId = measureId;
+ }
+
+ public Integer getSnapshotId() {
+ return snapshotId;
+ }
+
+ public void setSnapshotId(Integer snapshotId) {
+ this.snapshotId = snapshotId;
+ }
+
+ public Date getMeasureDate() {
+ return measureDate;
+ }
+
+ public void setMeasureDate(Date measureDate) {
+ this.measureDate = measureDate;
+ }
+
+ public Date getSnapshotDate() {
+ return snapshotDate;
+ }
+
+ public void setSnapshotDate(Date snapshotDate) {
+ this.snapshotDate = snapshotDate;
+ }
+
+ public Integer getMetricId() {
+ return metricId;
+ }
+
+ public void setMetricId(Integer metricId) {
+ this.metricId = metricId;
+ }
+
+ public Integer getProjectId() {
+ return projectId;
+ }
+
+ public void setProjectId(Integer projectId) {
+ this.projectId = projectId;
+ }
+
+ public void setMeasure(MeasureModel measure) {
+ setMeasureId(measure.getId());
+ setMeasureDate(measure.getMeasureDate());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof AsyncMeasureSnapshot)) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ AsyncMeasureSnapshot other = (AsyncMeasureSnapshot) obj;
+ return new EqualsBuilder()
+ .append(measureId, other.getMeasureId())
+ .append(measureDate, other.getMeasureDate())
+ .append(snapshotId, other.getSnapshotId())
+ .append(snapshotDate, other.getSnapshotDate())
+ .isEquals();
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(17, 37)
+ .append(measureId)
+ .append(measureDate)
+ .append(snapshotDate)
+ .append(snapshotId)
+ .toHashCode();
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("id", getId())
+ .append("measureId", measureId)
+ .append("measureDate", measureDate)
+ .append("snapshotId", snapshotId)
+ .append("snapshotDate", snapshotDate)
+ .toString();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureData.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureData.java
new file mode 100644
index 00000000000..f5b3ecd4694
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureData.java
@@ -0,0 +1,88 @@
+/*
+ * 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.database.model;
+
+import org.sonar.api.database.BaseIdentifiable;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "measure_data")
+public class MeasureData extends BaseIdentifiable {
+
+ @OneToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "measure_id")
+ private MeasureModel measure;
+
+ @Column(name = "snapshot_id", updatable = true, nullable = true)
+ private Integer snapshotId;
+
+ @Column(name = "data", updatable = true, nullable = true, length = 167772150)
+ private byte[] data;
+
+ public MeasureData(MeasureModel measure) {
+ this.measure = measure;
+ }
+
+ public MeasureData(MeasureModel measure, byte[] data) {
+ this.measure = measure;
+ this.data = data;
+ }
+
+ public MeasureData(MeasureModel measure, String dataString) {
+ this.measure = measure;
+ this.data = dataString.getBytes();
+ }
+
+ public MeasureData() {
+ }
+
+ public MeasureModel getMeasure() {
+ return measure;
+ }
+
+ public void setMeasure(MeasureModel measure) {
+ this.measure = measure;
+ }
+
+ public byte[] getData() {
+ return data;
+ }
+
+ public String getText() {
+ if (data != null) {
+ return new String(data);
+ }
+ return null;
+ }
+
+ public void setData(byte[] data) {
+ this.data = data;
+ }
+
+ public Integer getSnapshotId() {
+ return snapshotId;
+ }
+
+ public void setSnapshotId(Integer snapshotId) {
+ this.snapshotId = snapshotId;
+ }
+}
+
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureModel.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureModel.java
new file mode 100644
index 00000000000..d7f4ea4c22d
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/MeasureModel.java
@@ -0,0 +1,659 @@
+/*
+ * 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.database.model;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.RuleMeasure;
+import org.sonar.api.qualitymodel.Characteristic;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RulePriority;
+
+import javax.persistence.*;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * This class is the Hibernate model to store a measure in the DB
+ */
+@Entity
+@Table(name = "project_measures")
+public class MeasureModel implements Cloneable {
+
+ public static final int TEXT_VALUE_LENGTH = 96;
+
+ @Id
+ @Column(name = "id")
+ @GeneratedValue
+ private Long id;
+
+ @Column(name = "value", updatable = true, nullable = true, precision = 30, scale = 20)
+ private Double value = 0.0;
+
+ @Column(name = "text_value", updatable = true, nullable = true, length = TEXT_VALUE_LENGTH)
+ private String textValue;
+
+ @Column(name = "tendency", updatable = true, nullable = true)
+ private Integer tendency;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "metric_id")
+ @Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
+ private Metric metric;
+
+ @Column(name = "snapshot_id", updatable = true, nullable = true)
+ private Integer snapshotId;
+
+ @Column(name = "project_id", updatable = true, nullable = true)
+ private Integer projectId;
+
+ @Column(name = "description", updatable = true, nullable = true, length = 4000)
+ private String description;
+
+ @Temporal(TemporalType.TIMESTAMP)
+ @Column(name = "measure_date", updatable = true, nullable = true)
+ private Date measureDate;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "rule_id")
+ @Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
+ private Rule rule;
+
+ @Column(name = "rules_category_id")
+ private Integer rulesCategoryId;
+
+ @Column(name = "rule_priority", updatable = false, nullable = true)
+ @Enumerated(EnumType.ORDINAL)
+ private RulePriority rulePriority;
+
+ @Column(name = "alert_status", updatable = true, nullable = true, length = 5)
+ private String alertStatus;
+
+ @Column(name = "alert_text", updatable = true, nullable = true, length = 4000)
+ private String alertText;
+
+ @Column(name = "diff_value_1", updatable = true, nullable = true)
+ private Double diffValue1;
+
+ @Column(name = "diff_value_2", updatable = true, nullable = true)
+ private Double diffValue2;
+
+ @Column(name = "diff_value_3", updatable = true, nullable = true)
+ private Double diffValue3;
+
+ @Column(name = "url", updatable = true, nullable = true, length = 2000)
+ private String url;
+
+ @OneToMany(mappedBy = "measure", fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE})
+ private List<MeasureData> measureData = new ArrayList<MeasureData>();
+
+ @ManyToOne(fetch = FetchType.EAGER)
+ @JoinColumn(name = "characteristic_id")
+ private Characteristic characteristic;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ /**
+ * Creates a measure based on a metric and a double value
+ */
+ public MeasureModel(Metric metric, Double val) {
+ if (val.isNaN() || val.isInfinite()) {
+ throw new IllegalArgumentException("Measure value is NaN. Metric=" + metric);
+ }
+ this.metric = metric;
+ this.value = val;
+ }
+
+ /**
+ * Creates a measure based on a metric and an alert level
+ */
+ public MeasureModel(Metric metric, Metric.Level level) {
+ this.metric = metric;
+ if (level != null) {
+ this.textValue = level.toString();
+ }
+ }
+
+ /**
+ * Creates a measure based on a metric and a string value
+ */
+ public MeasureModel(Metric metric, String val) {
+ this.metric = metric;
+ setData(val);
+ }
+
+ /**
+ * Creates an empty measure
+ */
+ public MeasureModel() {
+ }
+
+ /**
+ * @return the measure double value
+ */
+ public Double getValue() {
+ return value;
+ }
+
+ /**
+ * @return the measure description
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Sets the measure description
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ /**
+ * Sets the measure value
+ *
+ * @throws IllegalArgumentException in case value is not a valid double
+ */
+ public MeasureModel setValue(Double value) throws IllegalArgumentException {
+ if (value != null && (value.isNaN() || value.isInfinite())) {
+ throw new IllegalArgumentException();
+ }
+ this.value = value;
+ return this;
+ }
+
+ /**
+ * @return the measure alert level
+ */
+ public Metric.Level getLevelValue() {
+ if (textValue != null) {
+ return Metric.Level.valueOf(textValue);
+ }
+ return null;
+ }
+
+ /**
+ * Use getData() instead
+ */
+ public String getTextValue() {
+ return textValue;
+ }
+
+ /**
+ * Use setData() instead
+ */
+ public void setTextValue(String textValue) {
+ this.textValue = textValue;
+ }
+
+ /**
+ * @return the measure tendency
+ */
+ public Integer getTendency() {
+ return tendency;
+ }
+
+ /**
+ * @return whether the measure is about rule
+ */
+ public boolean isRuleMeasure() {
+ return rule != null || rulePriority != null || rulesCategoryId != null;
+ }
+
+ /**
+ * Sets the measure tendency
+ *
+ * @return the current object
+ */
+ public MeasureModel setTendency(Integer tendency) {
+ this.tendency = tendency;
+ return this;
+ }
+
+ /**
+ * @return the measure metric
+ */
+ public Metric getMetric() {
+ return metric;
+ }
+
+ /**
+ * Sets the measure metric
+ */
+ public void setMetric(Metric metric) {
+ this.metric = metric;
+ }
+
+ /**
+ * @return the snapshot id the measure is attached to
+ */
+ public Integer getSnapshotId() {
+ return snapshotId;
+ }
+
+ /**
+ * Sets the snapshot id
+ *
+ * @return the current object
+ */
+ public MeasureModel setSnapshotId(Integer snapshotId) {
+ this.snapshotId = snapshotId;
+ return this;
+ }
+
+ /**
+ * @return the rule
+ */
+ public Rule getRule() {
+ return rule;
+ }
+
+ /**
+ * Sets the rule for the measure
+ *
+ * @return the current object
+ */
+ public MeasureModel setRule(Rule rule) {
+ this.rule = rule;
+ return this;
+ }
+
+ /**
+ * @return the rule category id
+ */
+ public Integer getRulesCategoryId() {
+ return rulesCategoryId;
+ }
+
+ /**
+ * Sets the rule category id
+ *
+ * @return the current object
+ */
+ public MeasureModel setRulesCategoryId(Integer id) {
+ this.rulesCategoryId = id;
+ return this;
+ }
+
+ /**
+ * @return the rule priority
+ */
+ public RulePriority getRulePriority() {
+ return rulePriority;
+ }
+
+ /**
+ * Sets the rule priority
+ */
+ public void setRulePriority(RulePriority rulePriority) {
+ this.rulePriority = rulePriority;
+ }
+
+ /**
+ * @return the project id
+ */
+ public Integer getProjectId() {
+ return projectId;
+ }
+
+ /**
+ * Sets the project id
+ */
+ public void setProjectId(Integer projectId) {
+ this.projectId = projectId;
+ }
+
+ /**
+ * @return the date of the measure
+ */
+ public Date getMeasureDate() {
+ return measureDate;
+ }
+
+ /**
+ * Sets the date for the measure
+ *
+ * @return the current object
+ */
+ public MeasureModel setMeasureDate(Date measureDate) {
+ this.measureDate = measureDate;
+ return this;
+ }
+
+ /**
+ * @return the alert status if there is one, null otherwise
+ */
+ public Metric.Level getAlertStatus() {
+ if (alertStatus == null) {
+ return null;
+ }
+ return Metric.Level.valueOf(alertStatus);
+ }
+
+ /**
+ * Sets the measure alert status
+ *
+ * @return the current object
+ */
+ public MeasureModel setAlertStatus(Metric.Level level) {
+ if (level != null) {
+ this.alertStatus = level.toString();
+ } else {
+ this.alertStatus = null;
+ }
+ return this;
+ }
+
+ /**
+ * @return the measure data
+ */
+ public String getData() {
+ if (this.textValue != null) {
+ return this.textValue;
+ }
+ if (metric.isDataType() && !measureData.isEmpty()) {
+ return measureData.get(0).getText();
+ }
+ return null;
+ }
+
+ /**
+ * Sets the measure data
+ */
+ public final void setData(String data) {
+ if (data == null) {
+ this.textValue = null;
+ measureData.clear();
+
+ } else {
+ if (data.length() > TEXT_VALUE_LENGTH) {
+ measureData.clear();
+ measureData.add(new MeasureData(this, data));
+
+ } else {
+ this.textValue = data;
+ }
+ }
+ }
+
+ /**
+ * Use getData() instead
+ */
+ public MeasureData getMeasureData() {
+ if (!measureData.isEmpty()) {
+ return measureData.get(0);
+ }
+ return null;
+ }
+
+ /**
+ * Use setData() instead
+ */
+ //@Deprecated
+ public void setMeasureData(MeasureData data) {
+ measureData.clear();
+ if (data != null) {
+ this.measureData.add(data);
+ }
+ }
+
+ /**
+ * @return the text of the alert
+ */
+ public String getAlertText() {
+ return alertText;
+ }
+
+ /**
+ * Sets the text for the alert
+ */
+ public void setAlertText(String alertText) {
+ this.alertText = alertText;
+ }
+
+ /**
+ * @return the measure URL
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * Sets the measure URL
+ */
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).
+ append("value", value).
+ append("metric", metric).
+ toString();
+ }
+
+ /**
+ * @return the rule id of the measure
+ */
+ public Integer getRuleId() {
+ if (getRule() != null) {
+ return getRule().getId();
+ }
+ return null;
+ }
+
+ /**
+ * @return diffValue1
+ */
+ public Double getDiffValue1() {
+ return diffValue1;
+ }
+
+ /**
+ * Sets the diffValue1
+ */
+ public void setDiffValue1(Double diffValue1) {
+ this.diffValue1 = diffValue1;
+ }
+
+ /**
+ * @return diffValue2
+ */
+ public Double getDiffValue2() {
+ return diffValue2;
+ }
+
+ /**
+ * Sets the diffValue2
+ */
+ public void setDiffValue2(Double diffValue2) {
+ this.diffValue2 = diffValue2;
+ }
+
+ /**
+ * @return diffValue3
+ */
+ public Double getDiffValue3() {
+ return diffValue3;
+ }
+
+ /**
+ * Sets the diffValue3
+ */
+ public void setDiffValue3(Double diffValue3) {
+ this.diffValue3 = diffValue3;
+ }
+
+ /**
+ * Saves the current object to database
+ *
+ * @return the current object
+ */
+ public MeasureModel save(DatabaseSession session) {
+ this.metric = session.reattach(Metric.class, metric.getId());
+ MeasureData data = getMeasureData();
+ setMeasureData(null);
+ session.save(this);
+
+ if (data != null) {
+ data.setMeasure(session.getEntity(MeasureModel.class, getId()));
+ data.setSnapshotId(snapshotId);
+ session.save(data);
+ setMeasureData(data);
+ }
+ return this;
+ }
+
+ public Characteristic getCharacteristic() {
+ return characteristic;
+ }
+
+ public MeasureModel setCharacteristic(Characteristic c) {
+ this.characteristic = c;
+ return this;
+ }
+
+ @Override
+ public Object clone() {
+ MeasureModel clone = new MeasureModel();
+ clone.setMetric(getMetric());
+ clone.setDescription(getDescription());
+ clone.setTextValue(getTextValue());
+ clone.setAlertStatus(getAlertStatus());
+ clone.setAlertText(getAlertText());
+ clone.setTendency(getTendency());
+ clone.setDiffValue1(getDiffValue1());
+ clone.setDiffValue2(getDiffValue2());
+ clone.setDiffValue3(getDiffValue3());
+ clone.setValue(getValue());
+ clone.setRulesCategoryId(getRulesCategoryId());
+ clone.setRulePriority(getRulePriority());
+ clone.setRule(getRule());
+ clone.setSnapshotId(getSnapshotId());
+ clone.setMeasureDate(getMeasureDate());
+ clone.setUrl(getUrl());
+ clone.setCharacteristic(getCharacteristic());
+ return clone;
+ }
+
+/**
+ * True if other fields than 'value' are set.
+ */
+ public boolean hasOptionalData() {
+ return getAlertStatus()!=null ||
+ getAlertText()!=null ||
+ getDescription()!=null ||
+ getDiffValue1()!=null ||
+ getDiffValue2()!=null ||
+ getDiffValue3()!=null ||
+ getMeasureData()!=null ||
+ getTendency()!=null ||
+ getUrl()!=null;
+ }
+
+
+ /**
+ * Builds a MeasureModel from a Measure
+ */
+ public static MeasureModel build(Measure measure) {
+ return build(measure, new MeasureModel());
+ }
+
+ /**
+ * Merges a Measure into a MeasureModel
+ */
+ public static MeasureModel build(Measure measure, MeasureModel merge) {
+ merge.setMetric(measure.getMetric());
+ merge.setDescription(measure.getDescription());
+ merge.setData(measure.getData());
+ merge.setAlertStatus(measure.getAlertStatus());
+ merge.setAlertText(measure.getAlertText());
+ merge.setTendency(measure.getTendency());
+ merge.setDiffValue1(measure.getDiffValue1());
+ merge.setDiffValue2(measure.getDiffValue2());
+ merge.setDiffValue3(measure.getDiffValue3());
+ merge.setUrl(measure.getUrl());
+ merge.setCharacteristic(measure.getCharacteristic());
+ if (measure.getValue() != null) {
+ merge.setValue(measure.getValue().doubleValue());
+ } else {
+ merge.setValue(null);
+ }
+ if (measure instanceof RuleMeasure) {
+ RuleMeasure ruleMeasure = (RuleMeasure) measure;
+ merge.setRulesCategoryId(ruleMeasure.getRuleCategory());
+ merge.setRulePriority(ruleMeasure.getRulePriority());
+ merge.setRule(ruleMeasure.getRule());
+ }
+ return merge;
+ }
+
+ /**
+ * @return a measure from the current object
+ */
+ public Measure toMeasure() {
+ Measure measure;
+ if (isRuleMeasure()) {
+ measure = new RuleMeasure(getMetric(), getRule(), getRulePriority(), getRulesCategoryId());
+ } else {
+ measure = new Measure(getMetric());
+ }
+ measure.setId(getId());
+ measure.setDescription(getDescription());
+ measure.setValue(getValue());
+ measure.setData(getData());
+ measure.setAlertStatus(getAlertStatus());
+ measure.setAlertText(getAlertText());
+ measure.setTendency(getTendency());
+ measure.setDiffValue1(getDiffValue1());
+ measure.setDiffValue2(getDiffValue2());
+ measure.setDiffValue3(getDiffValue3());
+ measure.setUrl(getUrl());
+ measure.setCharacteristic(getCharacteristic());
+ return measure;
+ }
+
+ /**
+ * Transforms a list of MeasureModel into a list of Measure
+ *
+ * @return an empty list if models is null
+ */
+ public static List<Measure> toMeasures(List<MeasureModel> models) {
+ List<Measure> result = new ArrayList<Measure>();
+ for (MeasureModel model : models) {
+ if (model != null) {
+ result.add(model.toMeasure());
+ }
+ }
+ return result;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/model/ResourceModel.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/ResourceModel.java
new file mode 100644
index 00000000000..bb8f1c13380
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/ResourceModel.java
@@ -0,0 +1,323 @@
+/*
+ * 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.database.model;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.hibernate.annotations.BatchSize;
+import org.sonar.api.database.BaseIdentifiable;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.resources.ProjectLink;
+import org.sonar.api.resources.Resource;
+
+import javax.persistence.*;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Class to map resource with hibernate model
+ */
+@Entity
+@Table(name = "projects")
+public class ResourceModel extends BaseIdentifiable implements Cloneable {
+
+ public static final String SCOPE_PROJECT = "PRJ";
+ public static final String QUALIFIER_PROJECT_TRUNK = "TRK";
+
+ public static final int DESCRIPTION_COLUMN_SIZE = 2000;
+ public static final int NAME_COLUMN_SIZE = 256;
+ public static final int KEY_SIZE = 400;
+
+ @Column(name = "name", updatable = true, nullable = true, length = NAME_COLUMN_SIZE)
+ private String name;
+
+ @Column(name = "long_name", updatable = true, nullable = true, length = NAME_COLUMN_SIZE)
+ private String longName;
+
+ @Column(name = "description", updatable = true, nullable = true, length = DESCRIPTION_COLUMN_SIZE)
+ private String description;
+
+ @Column(name = "enabled", updatable = true, nullable = false)
+ private Boolean enabled = Boolean.TRUE;
+
+ @Column(name = "scope", updatable = true, nullable = false, length = 3)
+ private String scope;
+
+ @Column(name = "qualifier", updatable = true, nullable = false, length = 3)
+ private String qualifier;
+
+ @Column(name = "kee", updatable = false, nullable = false, length = KEY_SIZE)
+ private String key;
+
+ @Column(name = "language", updatable = true, nullable = true, length = 5)
+ private String languageKey;
+
+ @Column(name = "root_id", updatable = true, nullable = true)
+ private Integer rootId;
+
+ @Column(name = "copy_resource_id", updatable = true, nullable = true)
+ private Integer copyResourceId;
+
+ @OneToMany(mappedBy = "resource", fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE})
+ @BatchSize(size = 8)
+ private List<ProjectLink> projectLinks = new ArrayList<ProjectLink>();
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "profile_id", updatable = true, nullable = true)
+ private RulesProfile rulesProfile;
+
+ /**
+ * Default constructor
+ */
+ public ResourceModel() {
+ }
+
+ /**
+ * <p>Creates a resource model</p>
+ *
+ * @param scope the scope the rule will apply on
+ * @param key the rule key. This is the name of the resource, including the path
+ * @param qualifier the resource qualifier
+ * @param rootId the rootId for the resource
+ * @param name the short name of the resource
+ */
+ public ResourceModel(String scope, String key, String qualifier, Integer rootId, String name) {
+ this.scope = scope;
+ this.key = key;
+ this.rootId = rootId;
+ this.name = name;
+ this.qualifier = qualifier;
+ }
+
+ /**
+ * Only available at project level.
+ */
+ public List<ProjectLink> getProjectLinks() {
+ return projectLinks;
+ }
+
+ public void setProjectLinks(List<ProjectLink> projectLinks) {
+ this.projectLinks = projectLinks;
+ }
+
+ /**
+ * @return a project link given its key if exists, null otherwise
+ */
+ public ProjectLink getProjectLink(String key) {
+ for (ProjectLink projectLink : projectLinks) {
+ if (key.equals(projectLink.getKey())) {
+ return projectLink;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Only available at project level.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Sets the resource description, truncated to DESCRIPTION_COLUMN_SIZE
+ */
+ public void setDescription(String description) {
+ this.description = StringUtils.abbreviate(description, DESCRIPTION_COLUMN_SIZE);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the resource name, truncated to NAME_COLUMN_SIZE
+ */
+ public void setName(String name) {
+ this.name = StringUtils.abbreviate(name, NAME_COLUMN_SIZE);
+ if (this.longName==null) {
+ this.longName = this.name;
+ }
+ }
+
+ public String getLongName() {
+ return longName;
+ }
+
+ /**
+ * Sets the long name of the resource, truncated to NAME_COLUMN_SIZE
+ */
+ public void setLongName(String s) {
+ if (StringUtils.isBlank(s)) {
+ this.longName = name;
+ } else {
+ this.longName = StringUtils.abbreviate(s, NAME_COLUMN_SIZE);
+ }
+ }
+
+ public Boolean getEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ }
+
+ public String getScope() {
+ return scope;
+ }
+
+ public void setScope(String scope) {
+ this.scope = scope;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getLanguageKey() {
+ return languageKey;
+ }
+
+ public void setLanguageKey(String lang) {
+ this.languageKey = lang;
+ }
+
+ public Integer getCopyResourceId() {
+ return copyResourceId;
+ }
+
+ public void setCopyResourceId(Integer copyResourceId) {
+ this.copyResourceId = copyResourceId;
+ }
+
+ /**
+ * @throws IllegalArgumentException if the key is longer than KEY_SIZE
+ */
+ public void setKey(String key) {
+ if (key.length() > KEY_SIZE) {
+ throw new IllegalArgumentException("Resource key is too long, max is " + KEY_SIZE + " characters. Got : " + key);
+ }
+ this.key = key;
+ }
+
+ public Integer getRootId() {
+ return rootId;
+ }
+
+ public void setRootId(Integer rootId) {
+ this.rootId = rootId;
+ }
+
+ public RulesProfile getRulesProfile() {
+ return rulesProfile;
+ }
+
+ public void setRulesProfile(RulesProfile rulesProfile) {
+ this.rulesProfile = rulesProfile;
+ }
+
+ public String getQualifier() {
+ return qualifier;
+ }
+
+ public void setQualifier(String qualifier) {
+ this.qualifier = qualifier;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ResourceModel)) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ ResourceModel other = (ResourceModel) obj;
+ return new EqualsBuilder()
+ .append(key, other.key)
+ .append(enabled, other.enabled)
+ .append(rootId, other.rootId)
+ .isEquals();
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(17, 37)
+ .append(key)
+ .append(enabled)
+ .append(rootId)
+ .toHashCode();
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("id", getId())
+ .append("key", key)
+ .append("scope", scope)
+ .append("qualifier", qualifier)
+ .append("name", name)
+ .append("longName", longName)
+ .append("lang", languageKey)
+ .append("enabled", enabled)
+ .append("rootId", rootId)
+ .append("copyResourceId", copyResourceId)
+ .toString();
+ }
+
+ @Override
+ public Object clone() {
+ ResourceModel clone = new ResourceModel(getScope(), getKey(), getQualifier(), getRootId(), getName());
+ clone.setDescription(getDescription());
+ clone.setEnabled(getEnabled());
+ clone.setProjectLinks(getProjectLinks());
+ clone.setRulesProfile(getRulesProfile());
+ clone.setLanguageKey(getLanguageKey());
+ clone.setCopyResourceId(getCopyResourceId());
+ clone.setLongName(getLongName());
+ return clone;
+ }
+
+ /**
+ * Maps a resource to a resource model and returns the resource
+ */
+ public static ResourceModel build(Resource resource) {
+ ResourceModel model = new ResourceModel();
+ model.setEnabled(Boolean.TRUE);
+ model.setDescription(resource.getDescription());
+ model.setKey(resource.getKey());
+ if (resource.getLanguage() != null) {
+ model.setLanguageKey(resource.getLanguage().getKey());
+ }
+ if (StringUtils.isNotBlank(resource.getName())) {
+ model.setName(resource.getName());
+ } else {
+ model.setName(resource.getKey());
+ }
+ model.setLongName(resource.getLongName());
+ model.setQualifier(resource.getQualifier());
+ model.setScope(resource.getScope());
+ return model;
+ }
+
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/model/RuleFailureModel.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/RuleFailureModel.java
new file mode 100644
index 00000000000..a9e73127507
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/RuleFailureModel.java
@@ -0,0 +1,135 @@
+/*
+ * 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.database.model;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ReflectionToStringBuilder;
+import org.sonar.api.database.BaseIdentifiable;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RulePriority;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "rule_failures")
+public class RuleFailureModel extends BaseIdentifiable {
+
+ public static final int MESSAGE_COLUMN_SIZE = 4000;
+
+ @Column(name = "snapshot_id")
+ protected Integer snapshotId;
+
+ @ManyToOne(fetch = FetchType.EAGER)
+ @JoinColumn(name = "rule_id")
+ private Rule rule;
+
+ @Column(name = "failure_level", updatable = false, nullable = false)
+ @Enumerated(EnumType.ORDINAL)
+ private RulePriority priority;
+
+ @Column(name = "message", updatable = false, nullable = true, length = MESSAGE_COLUMN_SIZE)
+ private String message;
+
+ @Column(name = "line", updatable = true, nullable = true)
+ private Integer line;
+
+ public RuleFailureModel() {
+ }
+
+ public RuleFailureModel(Rule rule, RulePriority priority) {
+ this.rule = rule;
+ this.priority = priority;
+ }
+
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = StringUtils.abbreviate(StringUtils.trim(message), MESSAGE_COLUMN_SIZE);
+ }
+
+ public RulePriority getLevel() {
+ return priority;
+ }
+
+ public void setLevel(RulePriority priority) {
+ this.priority = priority;
+ }
+
+ public Rule getRule() {
+ return rule;
+ }
+
+ public void setRule(Rule rule) {
+ this.rule = rule;
+ }
+
+ public Integer getLine() {
+ return line;
+ }
+
+ public RulePriority getPriority() {
+ return priority;
+ }
+
+ public Integer getSnapshotId() {
+ return snapshotId;
+ }
+
+ public void setSnapshotId(Integer snapshotId) {
+ this.snapshotId = snapshotId;
+ }
+
+ public void setPriority(RulePriority priority) {
+ this.priority = priority;
+ }
+
+ public void setLine(Integer line) {
+ this.line = line;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof RuleFailureModel)) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ RuleFailureModel other = (RuleFailureModel) obj;
+ return new EqualsBuilder()
+ .append(getId(), other.getId()).isEquals();
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(17, 37).
+ append(getId()).toHashCode();
+ }
+
+ @Override
+ public String toString() {
+ return ReflectionToStringBuilder.toString(this);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/model/Snapshot.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/Snapshot.java
new file mode 100644
index 00000000000..b0ec3039f16
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/Snapshot.java
@@ -0,0 +1,292 @@
+/*
+ * 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.database.model;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.database.BaseIdentifiable;
+import org.sonar.api.database.DatabaseSession;
+
+import java.util.Date;
+import javax.persistence.*;
+
+/**
+ * A class to map a snapshot with its hibernate model
+ */
+@Entity
+@Table(name = "snapshots")
+public class Snapshot extends BaseIdentifiable {
+
+ /**
+ * This status is set on the snapshot at the beginning of the batch
+ */
+ public final static String STATUS_UNPROCESSED = "U";
+
+ /**
+ * This status is set on the snapshot at the end of the batch
+ */
+ public final static String STATUS_PROCESSED = "P";
+
+ @Column(name = "project_id", updatable = true, nullable = true)
+ private Integer resourceId;
+
+ @Temporal(TemporalType.TIMESTAMP)
+ @Column(name = "created_at", updatable = true, nullable = true)
+ private Date createdAt;
+
+ @Column(name = "version", updatable = true, nullable = true, length = 60)
+ private String version;
+
+ @Column(name = "islast")
+ private Boolean last = Boolean.FALSE;
+
+ @Column(name = "status")
+ private String status = STATUS_UNPROCESSED;
+
+ @Column(name = "scope", updatable = true, nullable = true, length = 3)
+ private String scope;
+
+ @Column(name = "path", updatable = true, nullable = true, length = 96)
+ private String path;
+
+ @Column(name = "depth", updatable = true, nullable = true)
+ private Integer depth;
+
+ @Column(name = "qualifier", updatable = true, nullable = true, length = 3)
+ private String qualifier;
+
+ @Column(name = "root_snapshot_id", updatable = true, nullable = true)
+ private Integer rootId;
+
+ @Column(name = "parent_snapshot_id", updatable = true, nullable = true)
+ private Integer parentId;
+
+ @Column(name = "root_project_id", updatable = true, nullable = true)
+ private Integer rootProjectId;
+
+ public Snapshot() {
+
+ }
+
+ public Snapshot(ResourceModel resource, Snapshot parent) {
+ this.resourceId = resource.getId();
+ this.qualifier = resource.getQualifier();
+ this.scope = resource.getScope();
+
+ if (parent == null) {
+ path = "";
+ depth = 0;
+ this.createdAt = new Date();
+
+ } else {
+ this.parentId = parent.getId();
+ this.rootId = (parent.getRootId() == null ? parent.getId() : parent.getRootId());
+ this.createdAt = parent.getCreatedAt();
+ this.depth = parent.getDepth() + 1;
+ this.path = new StringBuilder()
+ .append(parent.getPath())
+ .append(parent.getId())
+ .append(".")
+ .toString();
+ }
+ this.rootProjectId = guessRootProjectId(resource, parent);
+ }
+
+ private static Integer guessRootProjectId(ResourceModel resource, Snapshot parent) {
+ Integer result;
+
+ // design problem : constants are defined in the Resource class, that should not be used by this class...
+ if ("TRK".equals(resource.getQualifier()) || "VW".equals(resource.getQualifier()) || "SVW".equals(resource.getQualifier())) {
+ result = resource.getCopyResourceId() != null ? resource.getCopyResourceId() : resource.getId();
+
+ } else if (parent == null) {
+ result = resource.getCopyResourceId() != null ? resource.getCopyResourceId() : resource.getId();
+
+ } else {
+ result = (parent.getRootProjectId() == null ? parent.getResourceId() : parent.getRootProjectId());
+ }
+ return result;
+ }
+
+ public Snapshot save(DatabaseSession session) {
+ return session.save(this);
+ }
+
+ public Snapshot(ResourceModel resource, boolean last, String status, Date date) {
+ this();
+ setResource(resource);
+ this.status = status;
+ this.last = last;
+ this.createdAt = date;
+ }
+
+ public Date getCreatedAt() {
+ return createdAt;
+ }
+
+ public void setCreatedAt(Date createdAt) {
+ this.createdAt = createdAt;
+ }
+
+ public Integer getResourceId() {
+ return resourceId;
+ }
+
+ public void setResourceId(Integer resourceId) {
+ this.resourceId = resourceId;
+ }
+
+ public final void setResource(ResourceModel resource) {
+ this.resourceId = resource.getId();
+ this.scope = resource.getScope();
+ this.qualifier = resource.getQualifier();
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public Integer getParentId() {
+ return parentId;
+ }
+
+ public void setParentId(Integer i) {
+ this.parentId = i;
+ }
+
+ public Boolean getLast() {
+ return last;
+ }
+
+ public void setLast(Boolean last) {
+ this.last = last;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public String getScope() {
+ return scope;
+ }
+
+ public void setScope(String scope) {
+ this.scope = scope;
+ }
+
+ public String getQualifier() {
+ return qualifier;
+ }
+
+ public void setQualifier(String qualifier) {
+ this.qualifier = qualifier;
+ }
+
+ public Integer getRootId() {
+ return rootId;
+ }
+
+ public void setRootId(Integer i) {
+ this.rootId = i;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public void setPath(String path) {
+ this.path = path;
+ }
+
+ public Integer getDepth() {
+ return depth;
+ }
+
+ public Integer getRootProjectId() {
+ return rootProjectId;
+ }
+
+ public void setRootProjectId(Integer rootProjectId) {
+ this.rootProjectId = rootProjectId;
+ }
+
+ /**
+ * Sets the depth of the snapshot
+ *
+ * @throws IllegalArgumentException when depth is negative
+ */
+ public void setDepth(Integer depth) {
+ if (depth != null && depth < 0) {
+ throw new IllegalArgumentException("depth can not be negative : " + depth);
+ }
+ this.depth = depth;
+ }
+
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Snapshot)) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ Snapshot other = (Snapshot) obj;
+ return new EqualsBuilder()
+ .append(resourceId, other.getResourceId())
+ .append(createdAt, other.getCreatedAt())
+ .isEquals();
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(17, 37)
+ .append(resourceId)
+ .append(createdAt)
+ .toHashCode();
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("id", getId())
+ .append("resourceId", resourceId)
+ .append("scope", scope)
+ .append("qualifier", qualifier)
+ .append("version", version)
+ .append("last", last)
+ .append("createdAt", createdAt)
+ .append("status", status)
+ .append("path", path)
+ .append("rootId", rootId)
+ .append("rootProjectId", rootProjectId)
+ .append("parentId", parentId)
+ .toString();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/model/SnapshotSource.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/SnapshotSource.java
new file mode 100644
index 00000000000..d0c12682172
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/SnapshotSource.java
@@ -0,0 +1,82 @@
+/*
+ * 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.database.model;
+
+import org.sonar.api.database.BaseIdentifiable;
+import org.sonar.api.database.DatabaseProperties;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Lob;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "snapshot_sources")
+public class SnapshotSource extends BaseIdentifiable {
+
+ @Column(name = "snapshot_id")
+ private Integer snapshotId;
+
+ @Lob
+ @Column(name = "data", updatable = true, nullable = true, length = DatabaseProperties.MAX_TEXT_SIZE)
+ private String data;
+
+ public SnapshotSource() {
+ }
+
+ public SnapshotSource(Snapshot snapshot, String source) {
+ this.snapshotId = snapshot.getId();
+ this.data = source;
+ }
+
+ public SnapshotSource(Integer snapshotId, String source) {
+ this.snapshotId = snapshotId;
+ this.data = source;
+ }
+
+ public void setSnapshot(Snapshot snapshot) {
+ this.snapshotId = snapshot.getId();
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof SnapshotSource)) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ SnapshotSource other = (SnapshotSource) obj;
+ return snapshotId.equals(other.snapshotId);
+ }
+
+ @Override
+ public int hashCode() {
+ return snapshotId.hashCode();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/database/model/User.java b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/User.java
new file mode 100644
index 00000000000..5ddb3703b00
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/database/model/User.java
@@ -0,0 +1,87 @@
+/*
+ * 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.database.model;
+
+import org.sonar.api.database.BaseIdentifiable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+/**
+ * @since 2.2
+ */
+@Entity
+@Table(name = "users")
+public class User extends BaseIdentifiable {
+
+ @Column(name = "login", updatable = true, nullable = true, length = 40)
+ private String login;
+
+ @Column(name = "name", updatable = true, nullable = true, length = 200)
+ private String name;
+
+ @Column(name = "email", updatable = true, nullable = true, length = 100)
+ private String email;
+
+ public String getLogin() {
+ return login;
+ }
+
+ public User setLogin(String login) {
+ this.login = login;
+ return this;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public User setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public String getEmail() {
+ return email;
+ }
+
+ public User setEmail(String email) {
+ this.email = email;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ User user = (User) o;
+ return login.equals(user.login);
+ }
+
+ @Override
+ public int hashCode() {
+ return login.hashCode();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/design/Dependency.java b/sonar-plugin-api/src/main/java/org/sonar/api/design/Dependency.java
new file mode 100644
index 00000000000..17c9c5de8d5
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/design/Dependency.java
@@ -0,0 +1,127 @@
+/*
+ * 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.design;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.resources.Resource;
+import org.sonar.graph.Edge;
+
+public class Dependency implements Edge<Resource> {
+
+ private Resource from;
+ private Resource to;
+ private String usage;
+ private int weight;
+ private Dependency parent;
+ private Long id;
+
+ public Dependency(Resource from, Resource to) {
+ if (from == null) {
+ throw new IllegalArgumentException("Dependency source is null");
+ }
+ if (to == null) {
+ throw new IllegalArgumentException("Dependency target is null");
+ }
+ this.from = from;
+ this.to = to;
+ }
+
+ public Resource getFrom() {
+ return from;
+ }
+
+ public Resource getTo() {
+ return to;
+ }
+
+ public String getUsage() {
+ return usage;
+ }
+
+ public Dependency setUsage(String usage) {
+ this.usage = usage;
+ return this;
+ }
+
+ public int getWeight() {
+ return weight;
+ }
+
+ public Dependency setWeight(int weight) {
+ this.weight = weight;
+ return this;
+ }
+
+ public Dependency getParent() {
+ return parent;
+ }
+
+ public Dependency setParent(Dependency parent) {
+ this.parent = parent;
+ return this;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ /**
+ * Internal use only.
+ */
+ public Dependency setId(Long id) {
+ this.id = id;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Dependency)) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ Dependency other = (Dependency) obj;
+ return new EqualsBuilder()
+ .append(from, other.from)
+ .append(to, other.to)
+ .isEquals();
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(17, 37)
+ .append(from)
+ .append(to)
+ .toHashCode();
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("from", from)
+ .append("to", to)
+ .append("weight", weight)
+ .append("usage", usage)
+ .toString();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/design/DependencyDto.java b/sonar-plugin-api/src/main/java/org/sonar/api/design/DependencyDto.java
new file mode 100644
index 00000000000..d29bee13fa2
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/design/DependencyDto.java
@@ -0,0 +1,207 @@
+/*
+ * 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.design;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "dependencies")
+public class DependencyDto {
+
+ @Id
+ @Column(name = "id")
+ @GeneratedValue
+ private Long id;
+
+ @Column(name = "from_snapshot_id", updatable = true, nullable = false)
+ private Integer fromSnapshotId;
+
+ @Column(name = "from_resource_id", updatable = true, nullable = false)
+ private Integer fromResourceId;
+
+ @Column(name = "from_scope", updatable = true, nullable = true)
+ private String fromScope;
+
+ @Column(name = "to_snapshot_id", updatable = true, nullable = false)
+ private Integer toSnapshotId;
+
+ @Column(name = "to_resource_id", updatable = true, nullable = false)
+ private Integer toResourceId;
+
+ @Column(name = "to_scope", updatable = true, nullable = true)
+ private String toScope;
+
+ @Column(name = "dep_weight", updatable = true, nullable = true)
+ private Integer weight;
+
+ @Column(name = "dep_usage", updatable = true, nullable = true, length = 30)
+ private String usage;
+
+ @Column(name = "project_snapshot_id", updatable = true, nullable = false)
+ private Integer projectSnapshotId;
+
+ @Column(name = "parent_dependency_id", updatable = true, nullable = true)
+ private Long parentDependencyId;
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Integer getFromSnapshotId() {
+ return fromSnapshotId;
+ }
+
+ public DependencyDto setFromSnapshotId(Integer fromSnapshotId) {
+ this.fromSnapshotId = fromSnapshotId;
+ return this;
+ }
+
+ public Integer getFromResourceId() {
+ return fromResourceId;
+ }
+
+ public DependencyDto setFromResourceId(Integer fromResourceId) {
+ this.fromResourceId = fromResourceId;
+ return this;
+ }
+
+ public Integer getToSnapshotId() {
+ return toSnapshotId;
+ }
+
+ public DependencyDto setToSnapshotId(Integer toSnapshotId) {
+ this.toSnapshotId = toSnapshotId;
+ return this;
+ }
+
+ public Integer getToResourceId() {
+ return toResourceId;
+ }
+
+ public DependencyDto setToResourceId(Integer toResourceId) {
+ this.toResourceId = toResourceId;
+ return this;
+ }
+
+ public Integer getWeight() {
+ return weight;
+ }
+
+ public DependencyDto setWeight(Integer weight) {
+ if (weight < 0) {
+ throw new IllegalArgumentException("Dependency weight can not be negative");
+ }
+ this.weight = weight;
+ return this;
+ }
+
+ public String getFromScope() {
+ return fromScope;
+ }
+
+ public DependencyDto setFromScope(String fromScope) {
+ this.fromScope = fromScope;
+ return this;
+ }
+
+ public String getToScope() {
+ return toScope;
+ }
+
+ public DependencyDto setToScope(String toScope) {
+ this.toScope = toScope;
+ return this;
+ }
+
+ public String getUsage() {
+ return usage;
+ }
+
+ public DependencyDto setUsage(String usage) {
+ this.usage = usage;
+ return this;
+ }
+
+ public Integer getProjectSnapshotId() {
+ return projectSnapshotId;
+ }
+
+ public DependencyDto setProjectSnapshotId(Integer projectSnapshotId) {
+ this.projectSnapshotId = projectSnapshotId;
+ return this;
+ }
+
+ public Long getParentDependencyId() {
+ return parentDependencyId;
+ }
+
+ public DependencyDto setParentDependencyId(Long parentDependencyId) {
+ this.parentDependencyId = parentDependencyId;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof DependencyDto)) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ DependencyDto other = (DependencyDto) obj;
+ return new EqualsBuilder()
+ .append(fromSnapshotId, other.fromSnapshotId)
+ .append(toSnapshotId, other.toSnapshotId)
+ .isEquals();
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(17, 37)
+ .append(fromSnapshotId)
+ .append(toSnapshotId)
+ .toHashCode();
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("id", getId())
+ .append("fromSnapshotId", fromSnapshotId)
+ .append("fromResourceId", fromResourceId)
+ .append("fromScope", fromScope)
+ .append("toSnapshotId", toSnapshotId)
+ .append("toResourceId", toResourceId)
+ .append("toScope", toScope)
+ .append("weight", weight)
+ .append("usage", usage)
+ .append("projectSnapshotId", projectSnapshotId)
+ .append("parentDependencyId", parentDependencyId)
+ .toString();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/AverageComplexityFormula.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/AverageComplexityFormula.java
new file mode 100644
index 00000000000..4b8bdb37b8f
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/AverageComplexityFormula.java
@@ -0,0 +1,80 @@
+/*
+ * 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.measures;
+
+import org.sonar.api.resources.ResourceUtils;
+
+import java.util.List;
+import java.util.Arrays;
+
+/**
+ * @since 2.1
+ */
+public class AverageComplexityFormula implements Formula {
+
+ private Metric byMetric;
+
+ /**
+ * @param byMetric The metric on which average complexity should be calculated : complexity by file, by method...
+ */
+ public AverageComplexityFormula(Metric byMetric) {
+ this.byMetric = byMetric;
+ }
+
+ public List<Metric> dependsUponMetrics() {
+ return Arrays.asList(CoreMetrics.COMPLEXITY, byMetric);
+ }
+
+ public Measure calculate(FormulaData data, FormulaContext context) {
+ if (!shouldDecorateResource(data, context)) {
+ return null;
+ }
+ if (ResourceUtils.isFile(context.getResource())) {
+ Double byMeasure = MeasureUtils.getValue(data.getMeasure(byMetric), null);
+ Double complexity = MeasureUtils.getValue(data.getMeasure(CoreMetrics.COMPLEXITY), null);
+ if (complexity != null && byMeasure != null && byMeasure > 0.0) {
+ return new Measure(context.getTargetMetric(), (complexity / byMeasure));
+ }
+ } else {
+ double totalByMeasure = 0;
+ double totalComplexity = 0;
+ boolean hasApplicableChildren = false;
+
+ for (FormulaData childrenData : data.getChildren()) {
+ Double childrenByMeasure = MeasureUtils.getValue(childrenData.getMeasure(byMetric), null);
+ Double childrenComplexity = MeasureUtils.getValue(childrenData.getMeasure(CoreMetrics.COMPLEXITY), null);
+ if (childrenComplexity != null && childrenByMeasure != null && childrenByMeasure > 0.0) {
+ totalByMeasure += childrenByMeasure;
+ totalComplexity += childrenComplexity;
+ hasApplicableChildren = true;
+ }
+ }
+ if (hasApplicableChildren) {
+ return new Measure(context.getTargetMetric(), (totalComplexity / totalByMeasure));
+ }
+ }
+ return null;
+ }
+
+ private boolean shouldDecorateResource(FormulaData data, FormulaContext context) {
+ return !MeasureUtils.hasValue(data.getMeasure(context.getTargetMetric()));
+ }
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
new file mode 100644
index 00000000000..8ddffc1ee22
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CoreMetrics.java
@@ -0,0 +1,445 @@
+/*
+ * 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.measures;
+
+import org.sonar.api.utils.SonarException;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @since 1.10
+ */
+public final class CoreMetrics {
+
+ private CoreMetrics() {
+ // only static stuff
+ }
+
+ public static final String DOMAIN_SIZE = "Size";
+ public static final String DOMAIN_TESTS = "Tests";
+ public static final String DOMAIN_COMPLEXITY = "Complexity";
+ public static final String DOMAIN_DOCUMENTATION = "Documentation";
+ public static final String DOMAIN_RULES = "Rules";
+ public static final String DOMAIN_RULE_CATEGORIES = "Rule categories";
+ public static final String DOMAIN_GENERAL = "General";
+ public static final String DOMAIN_DUPLICATION = "Duplication";
+ public static final String DOMAIN_DESIGN = "Design";
+
+ public static final String LINES_KEY = "lines";
+ public static final Metric LINES = new Metric(LINES_KEY, "Lines", "Lines", Metric.ValueType.INT, Metric.DIRECTION_WORST, false,
+ DOMAIN_SIZE).setFormula(new SumChildValuesFormula(false));
+
+ public static final String GENERATED_LINES_KEY = "generated_lines";
+ public static final Metric GENERATED_LINES = new Metric(GENERATED_LINES_KEY, "Generated Lines", "Number of generated lines",
+ Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_SIZE).setBestValue(0.0).setOptimizedBestValue(true).setFormula(
+ new SumChildValuesFormula(false));
+
+ public static final String NCLOC_KEY = "ncloc";
+ public static final Metric NCLOC = new Metric(NCLOC_KEY, "Lines of code", "Non Commenting Lines of Code", Metric.ValueType.INT,
+ Metric.DIRECTION_WORST, false, DOMAIN_SIZE).setFormula(new SumChildValuesFormula(false));
+
+ public static final String GENERATED_NCLOC_KEY = "generated_ncloc";
+ public static final Metric GENERATED_NCLOC = new Metric(GENERATED_NCLOC_KEY, "Generated lines of code",
+ "Generated non Commenting Lines of Code", Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_SIZE).setBestValue(0.0)
+ .setOptimizedBestValue(true).setFormula(new SumChildValuesFormula(false));
+
+ public static final String CLASSES_KEY = "classes";
+ public static final Metric CLASSES = new Metric(CLASSES_KEY, "Classes", "Classes", Metric.ValueType.INT, Metric.DIRECTION_WORST, false,
+ DOMAIN_SIZE).setFormula(new SumChildValuesFormula(false));
+
+ public static final String FILES_KEY = "files";
+ public static final Metric FILES = new Metric(FILES_KEY, "Files", "Number of files", Metric.ValueType.INT, Metric.DIRECTION_WORST, false,
+ DOMAIN_SIZE);
+
+ public static final String DIRECTORIES_KEY = "directories";
+ public static final Metric DIRECTORIES = new Metric(DIRECTORIES_KEY, "Directories", "Directories", Metric.ValueType.INT,
+ Metric.DIRECTION_WORST, false, DOMAIN_SIZE);
+
+ public static final String PACKAGES_KEY = "packages";
+ public static final Metric PACKAGES = new Metric(PACKAGES_KEY, "Packages", "Packages", Metric.ValueType.INT, Metric.DIRECTION_WORST,
+ false, DOMAIN_SIZE).setFormula(new SumChildValuesFormula(false));
+
+ public static final String FUNCTIONS_KEY = "functions";
+ public static final Metric FUNCTIONS = new Metric(FUNCTIONS_KEY, "Methods", "Methods", Metric.ValueType.INT, Metric.DIRECTION_WORST,
+ false, DOMAIN_SIZE).setFormula(new SumChildValuesFormula(false));
+
+ public static final String ACCESSORS_KEY = "accessors";
+ public static final Metric ACCESSORS = new Metric(ACCESSORS_KEY, "Accessors", "Accessors", Metric.ValueType.INT, Metric.DIRECTION_WORST,
+ false, DOMAIN_SIZE).setFormula(new SumChildValuesFormula(false));
+
+ public static final String PARAGRAPHS_KEY = "paragraphs";
+ public static final Metric PARAGRAPHS = new Metric(PARAGRAPHS_KEY, "Paragraphs", "Number of paragraphs", Metric.ValueType.INT,
+ Metric.DIRECTION_WORST, false, DOMAIN_SIZE).setFormula(new SumChildValuesFormula(false));
+
+ public static final String STATEMENTS_KEY = "statements";
+ public static final Metric STATEMENTS = new Metric(STATEMENTS_KEY, "Statements", "Number of statements", Metric.ValueType.INT,
+ Metric.DIRECTION_WORST, false, DOMAIN_SIZE).setFormula(new SumChildValuesFormula(false));
+
+ public static final String PUBLIC_API_KEY = "public_api";
+ public static final Metric PUBLIC_API = new Metric(PUBLIC_API_KEY, "Public API", "Public API", Metric.ValueType.INT,
+ Metric.DIRECTION_WORST, false, DOMAIN_SIZE).setFormula(new SumChildValuesFormula(false));
+
+ public static final String COMPLEXITY_KEY = "complexity";
+ public static final Metric COMPLEXITY = new Metric(COMPLEXITY_KEY, "Complexity", "Cyclomatic complexity", Metric.ValueType.INT,
+ Metric.DIRECTION_WORST, false, DOMAIN_COMPLEXITY).setFormula(new SumChildValuesFormula(false));
+
+ public static final String CLASS_COMPLEXITY_KEY = "class_complexity";
+ public static final Metric CLASS_COMPLEXITY = new Metric(CLASS_COMPLEXITY_KEY, "Complexity /class", "Complexity average by class",
+ Metric.ValueType.FLOAT, Metric.DIRECTION_WORST, true, DOMAIN_COMPLEXITY)
+ .setFormula(new AverageComplexityFormula(CoreMetrics.CLASSES));
+
+ public static final String FUNCTION_COMPLEXITY_KEY = "function_complexity";
+ public static final Metric FUNCTION_COMPLEXITY = new Metric(FUNCTION_COMPLEXITY_KEY, "Complexity /method",
+ "Complexity average by method", Metric.ValueType.FLOAT, Metric.DIRECTION_WORST, true, DOMAIN_COMPLEXITY)
+ .setFormula(new AverageComplexityFormula(CoreMetrics.FUNCTIONS));
+
+ public static final String FILE_COMPLEXITY_KEY = "file_complexity";
+ public static final Metric FILE_COMPLEXITY = new Metric(FILE_COMPLEXITY_KEY, "Complexity /file", "Complexity average by file",
+ Metric.ValueType.FLOAT, Metric.DIRECTION_WORST, true, DOMAIN_COMPLEXITY).setFormula(new AverageComplexityFormula(CoreMetrics.FILES));
+
+ public static final String PARAGRAPH_COMPLEXITY_KEY = "paragraph_complexity";
+ public static final Metric PARAGRAPH_COMPLEXITY = new Metric(PARAGRAPH_COMPLEXITY_KEY, "Complexity /paragraph",
+ "Complexity average by paragraph", Metric.ValueType.FLOAT, Metric.DIRECTION_WORST, true, DOMAIN_COMPLEXITY)
+ .setFormula(new AverageComplexityFormula(CoreMetrics.PARAGRAPHS));
+
+ public static final String CLASS_COMPLEXITY_DISTRIBUTION_KEY = "class_complexity_distribution";
+ public static final Metric CLASS_COMPLEXITY_DISTRIBUTION = new Metric(CLASS_COMPLEXITY_DISTRIBUTION_KEY,
+ "Classes distribution /complexity", "Classes distribution /complexity", Metric.ValueType.DISTRIB, Metric.DIRECTION_NONE, true,
+ DOMAIN_COMPLEXITY).setFormula(new SumChildDistributionFormula());
+
+ public static final String FUNCTION_COMPLEXITY_DISTRIBUTION_KEY = "function_complexity_distribution";
+ public static final Metric FUNCTION_COMPLEXITY_DISTRIBUTION = new Metric(FUNCTION_COMPLEXITY_DISTRIBUTION_KEY,
+ "Functions distribution /complexity", "Functions distribution /complexity", Metric.ValueType.DISTRIB, Metric.DIRECTION_NONE, true,
+ DOMAIN_COMPLEXITY).setFormula(new SumChildDistributionFormula());
+
+ public static final String FILE_COMPLEXITY_DISTRIBUTION_KEY = "file_complexity_distribution";
+ public static final Metric FILE_COMPLEXITY_DISTRIBUTION = new Metric(FILE_COMPLEXITY_DISTRIBUTION_KEY, "Files distribution /complexity",
+ "Files distribution /complexity", Metric.ValueType.DISTRIB, Metric.DIRECTION_NONE, true, DOMAIN_COMPLEXITY)
+ .setFormula(new SumChildDistributionFormula());
+
+ public static final String PARAGRAPH_COMPLEXITY_DISTRIBUTION_KEY = "paragraph_complexity_distribution";
+ public static final Metric PARAGRAPH_COMPLEXITY_DISTRIBUTION = new Metric(PARAGRAPH_COMPLEXITY_DISTRIBUTION_KEY,
+ "Paragraph distribution /complexity", "Paragraph distribution /complexity", Metric.ValueType.DISTRIB, Metric.DIRECTION_NONE, true,
+ DOMAIN_COMPLEXITY).setFormula(new SumChildDistributionFormula());
+
+ public static final String COMMENT_LINES_KEY = "comment_lines";
+ public static final Metric COMMENT_LINES = new Metric(COMMENT_LINES_KEY, "Comment lines", "Number of comment lines",
+ Metric.ValueType.INT, Metric.DIRECTION_BETTER, false, DOMAIN_DOCUMENTATION).setFormula(new SumChildValuesFormula(false));
+
+ public static final String COMMENT_LINES_DENSITY_KEY = "comment_lines_density";
+ public static final Metric COMMENT_LINES_DENSITY = new Metric(COMMENT_LINES_DENSITY_KEY, "Comments (%)",
+ "Comments balanced by ncloc + comment lines", Metric.ValueType.PERCENT, Metric.DIRECTION_BETTER, true, DOMAIN_DOCUMENTATION);
+
+ public static final String COMMENT_BLANK_LINES_KEY = "comment_blank_lines";
+ public static final Metric COMMENT_BLANK_LINES = new Metric(COMMENT_BLANK_LINES_KEY, "Blank comments",
+ "Comments that do not contain comments", Metric.ValueType.INT, Metric.DIRECTION_WORST, false, CoreMetrics.DOMAIN_DOCUMENTATION)
+ .setFormula(new SumChildValuesFormula(false)).setBestValue(0.0).setOptimizedBestValue(true);
+
+ public static final String PUBLIC_DOCUMENTED_API_DENSITY_KEY = "public_documented_api_density";
+ public static final Metric PUBLIC_DOCUMENTED_API_DENSITY = new Metric(PUBLIC_DOCUMENTED_API_DENSITY_KEY, "Public documented API (%)",
+ "Public documented classes and methods balanced by ncloc", Metric.ValueType.PERCENT, Metric.DIRECTION_BETTER, true,
+ DOMAIN_DOCUMENTATION).setWorstValue(0.0).setBestValue(100.0).setOptimizedBestValue(true);
+
+ public static final String PUBLIC_UNDOCUMENTED_API_KEY = "public_undocumented_api";
+ public static final Metric PUBLIC_UNDOCUMENTED_API = new Metric(PUBLIC_UNDOCUMENTED_API_KEY, "Public undocumented API",
+ "Public undocumented classes, methods and variables", Metric.ValueType.INT, Metric.DIRECTION_WORST, true, DOMAIN_DOCUMENTATION)
+ .setWorstValue(100.0).setBestValue(0.0).setDirection(Metric.DIRECTION_WORST).setOptimizedBestValue(true).setFormula(
+ new SumChildValuesFormula(false));
+
+ public static final String COMMENTED_OUT_CODE_LINES_KEY = "commented_out_code_lines";
+ public static final Metric COMMENTED_OUT_CODE_LINES = new Metric(COMMENTED_OUT_CODE_LINES_KEY, "Commented LOCs",
+ "Commented lines of code", Metric.ValueType.INT, Metric.DIRECTION_WORST, true, DOMAIN_DOCUMENTATION).setFormula(
+ new SumChildValuesFormula(false)).setBestValue(0.0).setOptimizedBestValue(true);
+
+ /* unit tests */
+ public static final String TESTS_KEY = "tests";
+ public static final Metric TESTS = new Metric(TESTS_KEY, "Unit tests", "Number of unit tests", Metric.ValueType.INT,
+ Metric.DIRECTION_WORST, false, DOMAIN_TESTS);
+
+ public static final String TEST_EXECUTION_TIME_KEY = "test_execution_time";
+ public static final Metric TEST_EXECUTION_TIME = new Metric(TEST_EXECUTION_TIME_KEY, "Unit tests duration",
+ "Execution duration of unit tests ", Metric.ValueType.MILLISEC, Metric.DIRECTION_WORST, false, DOMAIN_TESTS);
+
+ public static final String TEST_ERRORS_KEY = "test_errors";
+ public static final Metric TEST_ERRORS = new Metric(TEST_ERRORS_KEY, "Unit test errors", "Number of unit test errors",
+ Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_TESTS).setBestValue(0.0).setOptimizedBestValue(true);
+ public static final String SKIPPED_TESTS_KEY = "skipped_tests";
+ public static final Metric SKIPPED_TESTS = new Metric(SKIPPED_TESTS_KEY, "Skipped unit tests", "Number of skipped unit tests",
+ Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_TESTS).setBestValue(0.0).setOptimizedBestValue(true);
+ public static final String TEST_FAILURES_KEY = "test_failures";
+ public static final Metric TEST_FAILURES = new Metric(TEST_FAILURES_KEY, "Unit test failures", "Number of unit test failures",
+ Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_TESTS).setBestValue(0.0).setOptimizedBestValue(true);
+ public static final String TEST_SUCCESS_DENSITY_KEY = "test_success_density";
+ public static final Metric TEST_SUCCESS_DENSITY = new Metric(TEST_SUCCESS_DENSITY_KEY, "Unit test success (%)",
+ "Density of successful unit tests", Metric.ValueType.PERCENT, Metric.DIRECTION_BETTER, true, DOMAIN_TESTS).setWorstValue(0.0)
+ .setBestValue(100.0).setOptimizedBestValue(true);
+ public static final String TEST_DATA_KEY = "test_data";
+ public static final Metric TEST_DATA = new Metric(TEST_DATA_KEY, "Unit tests details", "Unit tests details", Metric.ValueType.DATA,
+ Metric.DIRECTION_WORST, false, DOMAIN_TESTS);
+
+ public static final String COVERAGE_KEY = "coverage";
+ public static final Metric COVERAGE = new Metric(COVERAGE_KEY, "Coverage", "Coverage by unit tests", Metric.ValueType.PERCENT,
+ Metric.DIRECTION_BETTER, true, DOMAIN_TESTS).setWorstValue(0.0).setBestValue(100.0);
+
+ public static final String LINES_TO_COVER_KEY = "lines_to_cover";
+ public static final Metric LINES_TO_COVER = new Metric(LINES_TO_COVER_KEY, "Lines to cover", "Lines to cover", Metric.ValueType.INT,
+ Metric.DIRECTION_BETTER, false, DOMAIN_TESTS).setFormula(new SumChildValuesFormula(false)).setHidden(true);
+
+ public static final String UNCOVERED_LINES_KEY = "uncovered_lines";
+ public static final Metric UNCOVERED_LINES = new Metric(UNCOVERED_LINES_KEY, "Uncovered lines", "Uncovered lines", Metric.ValueType.INT,
+ Metric.DIRECTION_WORST, false, DOMAIN_TESTS).setFormula(new SumChildValuesFormula(false));
+
+ public static final String LINE_COVERAGE_KEY = "line_coverage";
+ public static final Metric LINE_COVERAGE = new Metric(LINE_COVERAGE_KEY, "Line coverage", "Line coverage", Metric.ValueType.PERCENT,
+ Metric.DIRECTION_BETTER, true, DOMAIN_TESTS);
+
+ public static final String COVERAGE_LINE_HITS_DATA_KEY = "coverage_line_hits_data";
+ public static final Metric COVERAGE_LINE_HITS_DATA = new Metric(COVERAGE_LINE_HITS_DATA_KEY, "Coverage hits data",
+ "Code coverage line hits data", Metric.ValueType.DATA, Metric.DIRECTION_NONE, false, DOMAIN_TESTS);
+
+ public static final String CONDITIONS_TO_COVER_KEY = "conditions_to_cover";
+ public static final Metric CONDITIONS_TO_COVER = new Metric(CONDITIONS_TO_COVER_KEY, "Conditions to cover", "Conditions to cover",
+ Metric.ValueType.INT, Metric.DIRECTION_BETTER, false, DOMAIN_TESTS).setFormula(new SumChildValuesFormula(false)).setHidden(true);
+
+ public static final String UNCOVERED_CONDITIONS_KEY = "uncovered_conditions";
+ public static final Metric UNCOVERED_CONDITIONS = new Metric(UNCOVERED_CONDITIONS_KEY, "Uncovered conditions", "Uncovered conditions",
+ Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_TESTS).setFormula(new SumChildValuesFormula(false));
+
+ public static final String BRANCH_COVERAGE_KEY = "branch_coverage";
+ public static final Metric BRANCH_COVERAGE = new Metric(BRANCH_COVERAGE_KEY, "Branch coverage", "Branch coverage",
+ Metric.ValueType.PERCENT, Metric.DIRECTION_BETTER, true, DOMAIN_TESTS).setWorstValue(0.0).setBestValue(100.0);
+
+ public static final String BRANCH_COVERAGE_HITS_DATA_KEY = "branch_coverage_hits_data";
+ public static final Metric BRANCH_COVERAGE_HITS_DATA = new Metric(BRANCH_COVERAGE_HITS_DATA_KEY, "Branch coverage hits",
+ "Branch coverage hits", Metric.ValueType.DATA, Metric.DIRECTION_NONE, false, DOMAIN_TESTS);
+
+ /**
+ * @deprecated replaced since 1.11 by UNCOVERED_LINES and UNCOVERED_CONDITIONS
+ */
+ @Deprecated
+ public static final String UNCOVERED_COMPLEXITY_BY_TESTS_KEY = "uncovered_complexity_by_tests";
+ /**
+ * @deprecated replaced since 1.11 by UNCOVERED_LINES and UNCOVERED_CONDITIONS
+ */
+ @Deprecated
+ public static final Metric UNCOVERED_COMPLEXITY_BY_TESTS = new Metric(UNCOVERED_COMPLEXITY_BY_TESTS_KEY, "Uncovered complexity",
+ "Uncovered complexity", Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_COMPLEXITY).setFormula(new SumChildValuesFormula(
+ false));
+
+ public static final String DUPLICATED_LINES_KEY = "duplicated_lines";
+ public static final Metric DUPLICATED_LINES = new Metric(DUPLICATED_LINES_KEY, "Duplicated lines", "Duplicated lines",
+ Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_DUPLICATION).setBestValue(0.0).setOptimizedBestValue(true);
+
+ public static final String DUPLICATED_BLOCKS_KEY = "duplicated_blocks";
+ public static final Metric DUPLICATED_BLOCKS = new Metric(DUPLICATED_BLOCKS_KEY, "Duplicated blocks", "Duplicated blocks",
+ Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_DUPLICATION).setBestValue(0.0).setOptimizedBestValue(true);
+
+ public static final String DUPLICATED_FILES_KEY = "duplicated_files";
+ public static final Metric DUPLICATED_FILES = new Metric(DUPLICATED_FILES_KEY, "Duplicated files", "Duplicated files",
+ Metric.ValueType.INT, Metric.DIRECTION_WORST, true, DOMAIN_DUPLICATION).setBestValue(0.0).setOptimizedBestValue(true);
+
+ public static final String DUPLICATED_LINES_DENSITY_KEY = "duplicated_lines_density";
+ public static final Metric DUPLICATED_LINES_DENSITY = new Metric(DUPLICATED_LINES_DENSITY_KEY, "Duplicated lines (%)",
+ "Duplicated lines balanced by statements", Metric.ValueType.PERCENT, Metric.DIRECTION_WORST, true, DOMAIN_DUPLICATION).setWorstValue(
+ 50.0).setBestValue(0.0).setOptimizedBestValue(true);
+
+ public static final String DUPLICATIONS_DATA_KEY = "duplications_data";
+ public static final Metric DUPLICATIONS_DATA = new Metric(DUPLICATIONS_DATA_KEY, "Duplications details", "Duplications details",
+ Metric.ValueType.DATA, Metric.DIRECTION_NONE, false, DOMAIN_DUPLICATION);
+
+ /* coding rules */
+ public static final String USABILITY_KEY = "usability";
+ public static final Metric USABILITY = new Metric(USABILITY_KEY, "Usability", "Usability", Metric.ValueType.PERCENT,
+ Metric.DIRECTION_BETTER, true, DOMAIN_RULE_CATEGORIES).setBestValue(100.0).setOptimizedBestValue(true);
+ public static final String RELIABILITY_KEY = "reliability";
+ public static final Metric RELIABILITY = new Metric(RELIABILITY_KEY, "Reliability", "Reliability", Metric.ValueType.PERCENT,
+ Metric.DIRECTION_BETTER, true, DOMAIN_RULE_CATEGORIES).setBestValue(100.0).setOptimizedBestValue(true);
+ public static final String EFFICIENCY_KEY = "efficiency";
+ public static final Metric EFFICIENCY = new Metric(EFFICIENCY_KEY, "Efficiency", "Efficiency", Metric.ValueType.PERCENT,
+ Metric.DIRECTION_BETTER, true, DOMAIN_RULE_CATEGORIES).setBestValue(100.0).setOptimizedBestValue(true);
+ public static final String PORTABILITY_KEY = "portability";
+ public static final Metric PORTABILITY = new Metric(PORTABILITY_KEY, "Portability", "Portability", Metric.ValueType.PERCENT,
+ Metric.DIRECTION_BETTER, true, DOMAIN_RULE_CATEGORIES).setBestValue(100.0).setOptimizedBestValue(true);
+ public static final String MAINTAINABILITY_KEY = "maintainability";
+ public static final Metric MAINTAINABILITY = new Metric(MAINTAINABILITY_KEY, "Maintainability", "Maintainability",
+ Metric.ValueType.PERCENT, Metric.DIRECTION_BETTER, true, DOMAIN_RULE_CATEGORIES).setBestValue(100.0).setOptimizedBestValue(true);
+
+ public static final String WEIGHTED_VIOLATIONS_KEY = "weighted_violations";
+ public static final Metric WEIGHTED_VIOLATIONS = new Metric(WEIGHTED_VIOLATIONS_KEY, "Weighted violations", "Weighted Violations",
+ Metric.ValueType.INT, Metric.DIRECTION_WORST, true, DOMAIN_RULES).setHidden(true);
+
+ public static final String VIOLATIONS_DENSITY_KEY = "violations_density";
+ public static final Metric VIOLATIONS_DENSITY = new Metric(VIOLATIONS_DENSITY_KEY, "Rules compliance", "Rules compliance",
+ Metric.ValueType.PERCENT, Metric.DIRECTION_BETTER, true, DOMAIN_RULES);
+
+ public static final String VIOLATIONS_KEY = "violations";
+ public static final Metric VIOLATIONS = new Metric(VIOLATIONS_KEY, "Violations", "Violations", Metric.ValueType.INT,
+ Metric.DIRECTION_WORST, false, DOMAIN_RULES).setBestValue(0.0).setOptimizedBestValue(true);
+
+ public static final String BLOCKER_VIOLATIONS_KEY = "blocker_violations";
+ public static final Metric BLOCKER_VIOLATIONS = new Metric(BLOCKER_VIOLATIONS_KEY, "Blocker violations", "Blocker violations",
+ Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_RULES).setBestValue(0.0).setOptimizedBestValue(true);
+
+ public static final String CRITICAL_VIOLATIONS_KEY = "critical_violations";
+ public static final Metric CRITICAL_VIOLATIONS = new Metric(CRITICAL_VIOLATIONS_KEY, "Critical violations", "Critical violations",
+ Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_RULES).setBestValue(0.0).setOptimizedBestValue(true);
+
+ public static final String MAJOR_VIOLATIONS_KEY = "major_violations";
+ public static final Metric MAJOR_VIOLATIONS = new Metric(MAJOR_VIOLATIONS_KEY, "Major violations", "Major violations",
+ Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_RULES).setBestValue(0.0).setOptimizedBestValue(true);
+
+ public static final String MINOR_VIOLATIONS_KEY = "minor_violations";
+ public static final Metric MINOR_VIOLATIONS = new Metric(MINOR_VIOLATIONS_KEY, "Minor violations", "Minor violations",
+ Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_RULES).setBestValue(0.0).setOptimizedBestValue(true);
+
+ public static final String INFO_VIOLATIONS_KEY = "info_violations";
+ public static final Metric INFO_VIOLATIONS = new Metric(INFO_VIOLATIONS_KEY, "Info violations", "Info violations", Metric.ValueType.INT,
+ Metric.DIRECTION_WORST, false, DOMAIN_RULES).setBestValue(0.0).setOptimizedBestValue(true);
+
+ /* Design */
+
+ public static final String ABSTRACTNESS_KEY = "abstractness";
+ public static final Metric ABSTRACTNESS = new Metric(ABSTRACTNESS_KEY, "Abstractness", "Abstractness", Metric.ValueType.PERCENT,
+ Metric.DIRECTION_NONE, false, DOMAIN_DESIGN);
+ public static final String INSTABILITY_KEY = "instability";
+ public static final Metric INSTABILITY = new Metric(INSTABILITY_KEY, "Instability", "Instability", Metric.ValueType.PERCENT,
+ Metric.DIRECTION_NONE, false, DOMAIN_DESIGN);
+ public static final String DISTANCE_KEY = "distance";
+ public static final Metric DISTANCE = new Metric(DISTANCE_KEY, "Distance", "Distance", Metric.ValueType.FLOAT, Metric.DIRECTION_NONE,
+ false, DOMAIN_DESIGN);
+
+ public static final String DEPTH_IN_TREE_KEY = "dit";
+ public static final Metric DEPTH_IN_TREE = new Metric(DEPTH_IN_TREE_KEY, "Depth in Tree", "Depth in Inheritance Tree",
+ Metric.ValueType.INT, Metric.DIRECTION_NONE, false, DOMAIN_DESIGN);
+
+ public static final String NUMBER_OF_CHILDREN_KEY = "noc";
+ public static final Metric NUMBER_OF_CHILDREN = new Metric(NUMBER_OF_CHILDREN_KEY, "Number of Children", "Number of Children",
+ Metric.ValueType.INT, Metric.DIRECTION_NONE, false, DOMAIN_DESIGN);
+
+ public static final String RFC_KEY = "rfc";
+ public static final Metric RFC = new Metric(RFC_KEY, "RFC", "Response for Class", Metric.ValueType.INT, Metric.DIRECTION_WORST, false,
+ DOMAIN_DESIGN).setFormula(new WeightedMeanAggregationFormula(CoreMetrics.FILES, false));
+
+ public static final String RFC_DISTRIBUTION_KEY = "rfc_distribution";
+ public static final Metric RFC_DISTRIBUTION = new Metric(RFC_DISTRIBUTION_KEY, "Class distribution /RFC", "Class distribution /RFC",
+ Metric.ValueType.DISTRIB, Metric.DIRECTION_NONE, true, DOMAIN_DESIGN).setFormula(new SumChildDistributionFormula());
+
+ public static final String LCOM4_KEY = "lcom4";
+ public static final Metric LCOM4 = new Metric(LCOM4_KEY, "LCOM4", "Lack of Cohesion of Methods", Metric.ValueType.FLOAT,
+ Metric.DIRECTION_WORST, true, DOMAIN_DESIGN).setFormula(new WeightedMeanAggregationFormula(CoreMetrics.FILES, false));
+
+ public static final String LCOM4_BLOCKS_KEY = "lcom4_blocks";
+ public static final Metric LCOM4_BLOCKS = new Metric(LCOM4_BLOCKS_KEY, "LCOM4 blocks", "LCOM4 blocks", Metric.ValueType.DATA,
+ Metric.DIRECTION_NONE, false, DOMAIN_DESIGN).setHidden(true);
+
+ public static final String LCOM4_DISTRIBUTION_KEY = "lcom4_distribution";
+ public static final Metric LCOM4_DISTRIBUTION = new Metric(LCOM4_DISTRIBUTION_KEY, "Class distribution /LCOM4",
+ "Class distribution /LCOM4", Metric.ValueType.DISTRIB, Metric.DIRECTION_NONE, true, DOMAIN_DESIGN)
+ .setFormula(new SumChildDistributionFormula());
+
+ public static final String SUSPECT_LCOM4_DENSITY_KEY = "suspect_lcom4_density";
+ public static final Metric SUSPECT_LCOM4_DENSITY = new Metric(SUSPECT_LCOM4_DENSITY_KEY, "Suspect LCOM4 density",
+ "Density of classes having LCOM4>1", Metric.ValueType.PERCENT, Metric.DIRECTION_WORST, true, DOMAIN_DESIGN);
+
+ public static final String AFFERENT_COUPLINGS_KEY = "ca";
+ public static final Metric AFFERENT_COUPLINGS = new Metric(AFFERENT_COUPLINGS_KEY, "Afferent couplings", "Afferent couplings",
+ Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_DESIGN);
+ public static final String EFFERENT_COUPLINGS_KEY = "ce";
+ public static final Metric EFFERENT_COUPLINGS = new Metric(EFFERENT_COUPLINGS_KEY, "Efferent couplings", "Efferent couplings",
+ Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_DESIGN);
+
+ public static final String DEPENDENCY_MATRIX_KEY = "dsm";
+ public static final Metric DEPENDENCY_MATRIX = new Metric(DEPENDENCY_MATRIX_KEY, "Dependency Matrix", "Dependency Matrix",
+ Metric.ValueType.DATA, Metric.DIRECTION_NONE, false, DOMAIN_DESIGN);
+
+ public static final String PACKAGE_CYCLES_KEY = "package_cycles";
+ public static final Metric PACKAGE_CYCLES = new Metric(PACKAGE_CYCLES_KEY, "Package cycles", "Package cycles", Metric.ValueType.INT,
+ Metric.DIRECTION_WORST, true, DOMAIN_DESIGN).setFormula(new SumChildValuesFormula(false));
+
+ public static final String PACKAGE_TANGLE_INDEX_KEY = "package_tangle_index";
+ public static final Metric PACKAGE_TANGLE_INDEX = new Metric(PACKAGE_TANGLE_INDEX_KEY, "Package tangle index", "Package tangle index",
+ Metric.ValueType.PERCENT, Metric.DIRECTION_WORST, true, DOMAIN_DESIGN);
+
+ public static final String PACKAGE_TANGLES_KEY = "package_tangles";
+ public static final Metric PACKAGE_TANGLES = new Metric(PACKAGE_TANGLES_KEY, "File dependencies to cut", "File dependencies to cut",
+ Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_DESIGN).setFormula(new SumChildValuesFormula(false));
+
+ public static final String PACKAGE_FEEDBACK_EDGES_KEY = "package_feedback_edges";
+ public static final Metric PACKAGE_FEEDBACK_EDGES = new Metric(PACKAGE_FEEDBACK_EDGES_KEY, "Package dependencies to cut",
+ "Package dependencies to cut", Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_DESIGN)
+ .setFormula(new SumChildValuesFormula(false));
+
+ public static final String PACKAGE_EDGES_WEIGHT_KEY = "package_edges_weight";
+ public static final Metric PACKAGE_EDGES_WEIGHT = new Metric(PACKAGE_EDGES_WEIGHT_KEY, "Package edges weight", "Package edges weight",
+ Metric.ValueType.INT, Metric.DIRECTION_BETTER, false, DOMAIN_DESIGN).setFormula(new SumChildValuesFormula(false)).setHidden(true);
+
+ public static final String FILE_CYCLES_KEY = "file_cycles";
+ public static final Metric FILE_CYCLES = new Metric(FILE_CYCLES_KEY, "File cycles", "File cycles", Metric.ValueType.INT,
+ Metric.DIRECTION_WORST, true, DOMAIN_DESIGN).setHidden(true);
+
+ public static final String FILE_TANGLE_INDEX_KEY = "file_tangle_index";
+ public static final Metric FILE_TANGLE_INDEX = new Metric(FILE_TANGLE_INDEX_KEY, "File tangle index", "File tangle index",
+ Metric.ValueType.PERCENT, Metric.DIRECTION_WORST, true, DOMAIN_DESIGN).setHidden(true);
+
+ public static final String FILE_TANGLES_KEY = "file_tangles";
+ public static final Metric FILE_TANGLES = new Metric(FILE_TANGLES_KEY, "File tangles", "Files tangles", Metric.ValueType.INT,
+ Metric.DIRECTION_WORST, false, DOMAIN_DESIGN).setHidden(true);
+
+ public static final String FILE_FEEDBACK_EDGES_KEY = "file_feedback_edges";
+ public static final Metric FILE_FEEDBACK_EDGES = new Metric(FILE_FEEDBACK_EDGES_KEY, "Suspect file dependencies",
+ "Suspect file dependencies", Metric.ValueType.INT, Metric.DIRECTION_WORST, false, DOMAIN_DESIGN).setHidden(true);
+
+ public static final String FILE_EDGES_WEIGHT_KEY = "file_edges_weight";
+ public static final Metric FILE_EDGES_WEIGHT = new Metric(FILE_EDGES_WEIGHT_KEY, "File edges weight", "File edges weight",
+ Metric.ValueType.INT, Metric.DIRECTION_BETTER, false, DOMAIN_DESIGN).setHidden(true);
+
+ /* alerts */
+ public static final String ALERT_STATUS_KEY = "alert_status";
+ public static final Metric ALERT_STATUS = new Metric(ALERT_STATUS_KEY, "Alert", "Alert", Metric.ValueType.LEVEL, Metric.DIRECTION_BETTER,
+ true, DOMAIN_GENERAL);
+
+ /* quality profile */
+ public static final String PROFILE_KEY = "profile";
+ public static final Metric PROFILE = new Metric(PROFILE_KEY, "Profile", "Selected quality profile", Metric.ValueType.DATA,
+ Metric.DIRECTION_NONE, false, DOMAIN_GENERAL);
+
+ public static List<Metric> metrics = new ArrayList<Metric>();
+
+ public static Set<String> metricKeys = new HashSet<String>();
+
+ public static List<Metric> getMetrics() {
+ if (metrics.isEmpty()) {
+ for (Field field : CoreMetrics.class.getFields()) {
+ if (Metric.class.isAssignableFrom(field.getType())) {
+ try {
+ metrics.add((Metric) field.get(null));
+ } catch (IllegalAccessException e) {
+ throw new SonarException("can not load metrics from " + CoreMetrics.class.getSimpleName(), e);
+ }
+ }
+ }
+ }
+ return metrics;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/CountDistributionBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CountDistributionBuilder.java
new file mode 100644
index 00000000000..d8350e01398
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/CountDistributionBuilder.java
@@ -0,0 +1,164 @@
+/*
+ * 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.measures;
+
+import org.apache.commons.collections.SortedBag;
+import org.apache.commons.collections.bag.TreeBag;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.math.NumberUtils;
+import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.api.utils.SonarException;
+
+import java.util.Map;
+
+/**
+ * Utility to build a distribution based on discrete values
+ *
+ * <p>An example of usage : you wish to record the number of violations for each level of rules priority</p>
+ *
+ * @since 1.10
+ */
+public class CountDistributionBuilder implements MeasureBuilder {
+
+ private Metric metric;
+ private SortedBag countBag;
+
+ /**
+ * Creates an empty CountDistributionBuilder for a specified metric
+ *
+ * @param metric the metric
+ */
+ public CountDistributionBuilder(Metric metric) {
+ setMetric(metric);
+ this.countBag = new TreeBag();
+ }
+
+ /**
+ * Increments an entry
+ *
+ * @param value the value that should be incremented
+ * @param count the number by which to increment
+ * @return the current object
+ */
+ public CountDistributionBuilder add(Object value, int count) {
+ if (count == 0) {
+ addZero(value);
+
+ } else {
+ if (this.countBag.add(value, count)) {
+ this.countBag.add(value, 1);//hack
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Increments an entry by one
+ *
+ * @param value the value that should be incremented
+ * @return the current object
+ */
+ public CountDistributionBuilder add(Object value) {
+ return add(value, 1);
+ }
+
+ /**
+ * Adds an entry without a zero count if it does not exist
+ *
+ * @param value the entry to be added
+ * @return the current object
+ */
+ public CountDistributionBuilder addZero(Object value) {
+ if (!countBag.contains(value)) {
+ countBag.add(value, 1);
+ }
+ return this;
+ }
+
+ /**
+ * Adds an existing Distribution to the current one.
+ * It will create the entries if they don't exist.
+ * Can be used to add the values of children resources for example
+ *
+ * @param measure the measure to add to the current one
+ * @return the current object
+ */
+ public CountDistributionBuilder add(Measure measure) {
+ if (measure != null && measure.getData() != null) {
+ Map<String, String> map = KeyValueFormat.parse(measure.getData());
+ for (Map.Entry<String, String> entry : map.entrySet()) {
+ String key = entry.getKey();
+ int value = (StringUtils.isBlank(entry.getValue()) ? 0 : Integer.parseInt(entry.getValue()));
+ if (NumberUtils.isNumber(key)) {
+ add(NumberUtils.toInt(key), value);
+ } else {
+ add(key, value);
+ }
+ }
+ }
+ return this;
+ }
+
+ /**
+ * @return whether the current object is empty or not
+ */
+ public boolean isEmpty() {
+ return countBag.isEmpty();
+ }
+
+ /**
+ * Resets all entries to zero
+ *
+ * @return the current object
+ */
+ public CountDistributionBuilder clear() {
+ countBag.clear();
+ return this;
+ }
+
+ /**
+ * Shortcut for <code>build(true)</code>
+ *
+ * @return the built measure
+ */
+ public Measure build() {
+ return build(true);
+ }
+
+ /**
+ * Used to build a measure from the current object
+ *
+ * @param allowEmptyData should be built if current object is empty
+ * @return the built measure
+ */
+ public Measure build(boolean allowEmptyData) {
+ if (!isEmpty() || allowEmptyData) {
+ return new Measure(metric, KeyValueFormat.format(countBag, -1)); //-1 is a hack to include zero values
+ }
+ return null;
+ }
+
+ private void setMetric(Metric metric) {
+ if (metric == null || !metric.isDataType()) {
+ throw new SonarException("Metric is null or has unvalid type");
+ }
+ this.metric = metric;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Formula.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Formula.java
new file mode 100644
index 00000000000..8d93892204d
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Formula.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.measures;
+
+import java.util.List;
+
+/**
+ * @since 1.11
+ */
+public interface Formula {
+
+ List<Metric> dependsUponMetrics();
+
+ Measure calculate(FormulaData data, FormulaContext context);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/FormulaContext.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/FormulaContext.java
new file mode 100644
index 00000000000..6ca8ddcc9a6
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/FormulaContext.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.measures;
+
+import org.apache.commons.configuration.Configuration;
+import org.sonar.api.resources.Resource;
+
+/**
+ * @since 1.11
+ */
+public interface FormulaContext {
+
+ Metric getTargetMetric();
+
+ Resource getResource();
+
+ Configuration getConfiguration();
+
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/FormulaData.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/FormulaData.java
new file mode 100644
index 00000000000..003d4c63416
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/FormulaData.java
@@ -0,0 +1,38 @@
+/*
+ * 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.measures;
+
+import java.util.Collection;
+
+/**
+ * @since 1.11
+ */
+public interface FormulaData {
+
+ Measure getMeasure(Metric metric);
+
+ <M> M getMeasures(MeasuresFilter<M> filter);
+
+ Collection<Measure> getChildrenMeasures(MeasuresFilter filter);
+
+ Collection<Measure> getChildrenMeasures(Metric metric);
+
+ Collection<FormulaData> getChildren();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeanAggregationFormula.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeanAggregationFormula.java
new file mode 100644
index 00000000000..1e33d208154
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeanAggregationFormula.java
@@ -0,0 +1,63 @@
+/*
+ * 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.measures;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @since 2.0
+ */
+public class MeanAggregationFormula implements Formula {
+
+ private boolean forceZeroIfMissingData=false;
+
+ public MeanAggregationFormula(boolean forceZeroIfMissingData) {
+ this.forceZeroIfMissingData = forceZeroIfMissingData;
+ }
+
+ public MeanAggregationFormula() {
+ this(false);
+ }
+
+ public List<Metric> dependsUponMetrics() {
+ return Collections.emptyList();
+ }
+
+ public Measure calculate(FormulaData data, FormulaContext context) {
+ double sum=0.0;
+ int count=0;
+ boolean hasValue=false;
+ Collection<Measure> measures = data.getChildrenMeasures(context.getTargetMetric());
+ for (Measure measure : measures) {
+ if (MeasureUtils.hasValue(measure)) {
+ sum+=measure.getValue();
+ count++;
+ hasValue=true;
+ }
+ }
+
+ if (!hasValue && !forceZeroIfMissingData) {
+ return null;
+ }
+ return new Measure(context.getTargetMetric(), (count==0 ? 0.0 : sum/count));
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java
new file mode 100644
index 00000000000..ebf84ff0e2b
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Measure.java
@@ -0,0 +1,554 @@
+/*
+ * 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.measures;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.qualitymodel.Characteristic;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Date;
+
+/**
+ * A class to handle measures.
+ * <p/>
+ *
+ * @since 1.10
+ */
+public class Measure {
+ protected static final int MAX_TEXT_SIZE = 96;
+
+ /**
+ * Default precision when saving a float type metric
+ */
+ public final static int DEFAULT_PRECISION = 1;
+
+ private Long id; // for internal use
+ protected Metric metric;
+ protected Double value;
+ protected String data;
+ protected String description;
+ protected Metric.Level alertStatus;
+ protected String alertText;
+ protected Integer tendency;
+ protected Date date;
+ protected Double diff1, diff2, diff3;
+ protected String url;
+ protected Characteristic characteristic;
+ protected PersistenceMode persistenceMode = PersistenceMode.FULL;
+
+ /**
+ * Creates a measure with a metric
+ *
+ * @param metric the metric
+ */
+ public Measure(Metric metric) {
+ this.metric = metric;
+ }
+
+ /**
+ * Creates a measure with a metric and a value
+ *
+ * @param metric the metric
+ * @param value its value
+ */
+ public Measure(Metric metric, Double value) {
+ this.metric = metric;
+ setValue(value);
+ }
+
+ /**
+ * Creates a measure with a metric, a value and a precision for the value
+ *
+ * @param metric the metric
+ * @param value its value
+ * @param precision the value precision
+ */
+ public Measure(Metric metric, Double value, int precision) {
+ this.metric = metric;
+ setValue(value, precision);
+ }
+
+ /**
+ * Creates a measure with a metric, a value and a data field
+ *
+ * @param metric the metric
+ * @param value the value
+ * @param data the data field
+ */
+ public Measure(Metric metric, Double value, String data) {
+ this.metric = metric;
+ setValue(value);
+ setData(data);
+ }
+
+ /**
+ * * Creates a measure with a metric and a data field
+ *
+ * @param metric the metric
+ * @param data the data field
+ */
+ public Measure(Metric metric, String data) {
+ this.metric = metric;
+ setData(data);
+ }
+
+ /**
+ * Creates a measure with a metric and an alert level
+ *
+ * @param metric the metric
+ * @param level the alert level
+ */
+ public Measure(Metric metric, Metric.Level level) {
+ this.metric = metric;
+ if (level != null) {
+ this.data = level.toString();
+ }
+ }
+
+ /**
+ * Creates an empty measure
+ */
+ public Measure() {
+ }
+
+ /**
+ * Gets the persistence mode of the measure. Default persistence mode is FULL,
+ * except when instantiating the measure with a String parameter.
+ */
+ public PersistenceMode getPersistenceMode() {
+ return persistenceMode;
+ }
+
+ /**
+ * <p>Sets the persistence mode of a measure.</p>
+ * <p><b>WARNING : </b>Being able to reuse measures saved in memory is only possible within the same tree.
+ * In a multi-module project for example, a measure save in memory at the module level will not be accessible by
+ * the root project. In that case, database should be used.
+ * </p>
+ *
+ * @param mode the mode
+ * @return the measure object instance
+ */
+ public Measure setPersistenceMode(PersistenceMode mode) {
+ this.persistenceMode = mode;
+ return this;
+ }
+
+ /**
+ * @return return the measures underlying metric
+ */
+ public Metric getMetric() {
+ return metric;
+ }
+
+ /**
+ * Set the underlying metric
+ *
+ * @param metric the metric
+ * @return the measure object instance
+ */
+ public Measure setMetric(Metric metric) {
+ this.metric = metric;
+ return this;
+ }
+
+ /**
+ * @return transforms and returns the data fields as a level of alert
+ */
+ public Metric.Level getDataAsLevel() {
+ if (data != null) {
+ return Metric.Level.valueOf(data);
+ }
+ return null;
+ }
+
+ /**
+ * @return the date of the measure, i.e. the date the measure was taken. Used only in TimeMachine queries
+ */
+ public Date getDate() {
+ return date;
+ }
+
+ /**
+ * Sets the date of the measure - Used only in TimeMachine queries
+ *
+ * @param date the date
+ * @return the measure object instance
+ */
+ public Measure setDate(Date date) {
+ this.date = date;
+ return this;
+ }
+
+ /**
+ * @return the value of the measure as a double
+ */
+ public Double getValue() {
+ return value;
+ }
+
+ /**
+ * @return the value of the measure as an int
+ */
+ public Integer getIntValue() {
+ if (value == null) {
+ return null;
+ }
+ return value.intValue();
+ }
+
+ /**
+ * Sets the measure value with the default precision of 1
+ *
+ * @param v the measure value
+ * @return the measure object instance
+ */
+ public Measure setValue(Double v) {
+ return setValue(v, DEFAULT_PRECISION);
+ }
+
+ /**
+ * Sets the measure value as an int
+ *
+ * @param i the value
+ * @return the measure object instance
+ */
+ public Measure setIntValue(Integer i) {
+ if (i == null) {
+ this.value = null;
+ } else {
+ this.value = Double.valueOf(i);
+ }
+ return this;
+ }
+
+ /**
+ * Sets the measure value with a given precision
+ *
+ * @param v the measure value
+ * @param precision the measure value precision
+ * @return the measure object instance
+ */
+ public Measure setValue(Double v, int precision) {
+ if (v != null) {
+ if (Double.isNaN(v)) {
+ throw new IllegalArgumentException("Measure value can not be NaN");
+ }
+ this.value = scaleValue(v, precision);
+ } else {
+ this.value = null;
+ }
+ return this;
+ }
+
+ private double scaleValue(double value, int scale) {
+ BigDecimal bd = BigDecimal.valueOf(value);
+ return bd.setScale(scale, RoundingMode.HALF_UP).doubleValue();
+ }
+
+ /**
+ * @return the data field of the measure
+ */
+ public String getData() {
+ return data;
+ }
+
+ /**
+ * Sets the data field of the measure.
+ *
+ * @param s the data
+ * @return the measure object instance
+ */
+ public Measure setData(String s) {
+ if (s != null && s.length() >= MAX_TEXT_SIZE && !metric.isDataType()) {
+ throw new IllegalArgumentException("Data is too long for non-data metric : size=" + s.length() + ", max=" + MAX_TEXT_SIZE);
+ }
+ this.data = s;
+ return this;
+ }
+
+ /**
+ * Sets an alert level as the data field
+ *
+ * @param level the alert level
+ * @return the measure object instance
+ */
+ public Measure setData(Metric.Level level) {
+ if (level == null) {
+ this.data = null;
+ } else {
+ this.data = level.toString();
+ }
+ return this;
+ }
+
+ /**
+ * @return the description of the measure
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Sets the measure description
+ *
+ * @param description the description
+ * @return the measure object instance
+ */
+ public Measure setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * @return the alert status of the measure
+ */
+ public Metric.Level getAlertStatus() {
+ return alertStatus;
+ }
+
+ /**
+ * Set the alert status of the measure
+ *
+ * @param status the status
+ * @return the measure object instance
+ */
+ public Measure setAlertStatus(Metric.Level status) {
+ this.alertStatus = status;
+ return this;
+ }
+
+ /**
+ * @return the text associated to the alert on the measure
+ */
+ public String getAlertText() {
+ return alertText;
+ }
+
+ /**
+ * Sets the text associated to the alert on the measure
+ *
+ * @param alertText the text
+ * @return the measure object instance
+ */
+ public Measure setAlertText(String alertText) {
+ this.alertText = alertText;
+ return this;
+ }
+
+ /**
+ * Gets the measure tendency
+ *
+ * @return the tendency
+ */
+ public Integer getTendency() {
+ return tendency;
+ }
+
+ /**
+ * Sets the tendency for the measure
+ *
+ * @param tendency the tendency
+ * @return the measure object instance
+ */
+ public Measure setTendency(Integer tendency) {
+ this.tendency = tendency;
+ return this;
+ }
+
+ /**
+ * @return the measure id - Internal use only
+ */
+ public Long getId() {
+ return id;
+ }
+
+ /**
+ * Sets the measure id - Internal use only
+ *
+ * @param id the id
+ * @return the measure object instance
+ */
+ public Measure setId(Long id) {
+ this.id = id;
+ return this;
+ }
+
+ /**
+ * @return the first differential value of the measure
+ */
+ public Double getDiffValue1() {
+ return diff1;
+ }
+
+ /**
+ * Sets the first differential value of the measure
+ *
+ * @param diff1 the diff
+ * @return the measure object instance
+ */
+ public Measure setDiffValue1(Double diff1) {
+ this.diff1 = diff1;
+ return this;
+ }
+
+ /**
+ * @return the second differential value of the measure
+ */
+ public Double getDiffValue2() {
+ return diff2;
+ }
+
+ /**
+ * Sets the second differential value of the measure
+ *
+ * @param diff2 the diff
+ * @return the measure object instance
+ */
+ public Measure setDiffValue2(Double diff2) {
+ this.diff2 = diff2;
+ return this;
+ }
+
+ /**
+ * @return the third differential value of the measure
+ */
+ public Double getDiffValue3() {
+ return diff3;
+ }
+
+ /**
+ * Sets the third differential value of the measure
+ *
+ * @param diff3 the diff
+ * @return the measure object instance
+ */
+ public Measure setDiffValue3(Double diff3) {
+ this.diff3 = diff3;
+ return this;
+ }
+
+ /**
+ * @return the url of the measure
+ */
+ public String getUrl() {
+ return url;
+ }
+
+ /**
+ * Sets the URL of the measure
+ *
+ * @param url the url
+ * @return the measure object instance
+ */
+ public Measure setUrl(String url) {
+ this.url = url;
+ return this;
+ }
+
+ public final Characteristic getCharacteristic() {
+ return characteristic;
+ }
+
+ public final Measure setCharacteristic(Characteristic characteristic) {
+ this.characteristic = characteristic;
+ return this;
+ }
+
+
+// @Override
+// public boolean equals(Object obj) {
+// if (!(obj.getClass().equals(Measure.class))) {
+// return false;
+// }
+// if (this == obj) {
+// return true;
+// }
+// Measure rhs = (Measure) obj;
+// return new EqualsBuilder()
+// .append(metric, rhs.getMetric())
+// .append(characteristic, rhs.getCharacteristic())
+// .isEquals();
+// }
+//
+// @Override
+// public int hashCode() {
+// return (metric != null ? metric.hashCode() : 0);
+// }
+//
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o.getClass().equals(Measure.class))) {
+ return false;
+ }
+
+ Measure measure = (Measure) o;
+ if (metric != null ? !metric.equals(measure.metric) : measure.metric != null) {
+ return false;
+ }
+ if (characteristic != null ? !characteristic.equals(measure.characteristic) : measure.characteristic != null) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = metric != null ? metric.hashCode() : 0;
+ result = 31 * result + (characteristic != null ? characteristic.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).
+ append("id", id).
+ append("metric", metric).
+ append("value", value).
+ append("data", data).
+ append("description", description).
+ append("alertStatus", alertStatus).
+ append("alertText", alertText).
+ append("tendency", tendency).
+ append("characteristic", characteristic).
+ append("diff1", diff1).
+ append("diff2", diff2).
+ append("diff3", diff3).
+ toString();
+ }
+
+ public boolean hasOptionalData() {
+ return getAlertStatus() != null ||
+ getAlertText() != null ||
+ getDescription() != null ||
+ getDiffValue1() != null ||
+ getDiffValue2() != null ||
+ getDiffValue3() != null ||
+ getData() != null ||
+ getTendency() != null ||
+ getUrl() != null;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasureBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasureBuilder.java
new file mode 100644
index 00000000000..36630a8c515
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasureBuilder.java
@@ -0,0 +1,29 @@
+/*
+ * 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.measures;
+
+/**
+ * MeasureBuilder helps to build complex measures.
+ *
+ * @since 1.10
+ */
+public interface MeasureBuilder {
+ Measure build();
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasureUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasureUtils.java
new file mode 100644
index 00000000000..dae6e3d1154
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasureUtils.java
@@ -0,0 +1,135 @@
+/*
+ * 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.measures;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.util.Collection;
+
+/**
+ * An utility class to manipulate measures
+ *
+ * @since 1.10
+ */
+public final class MeasureUtils {
+
+ /**
+ * Class cannot be instantiated, it should only be access through static methods
+ */
+ private MeasureUtils() {
+ }
+
+ /**
+ * Return true if all measures have numeric value
+ *
+ * @param measures the measures
+ * @return true if all measures numeric values
+ */
+ public static boolean haveValues(Measure... measures) {
+ if (measures == null || measures.length == 0) {
+ return false;
+ }
+ for (Measure measure : measures) {
+ if (!hasValue(measure)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Get the value of a measure, or alternatively a default value
+ *
+ * @param measure the measure
+ * @param defaultValue the default value
+ * @return <code>defaultValue</code> if measure is null or has no values.
+ */
+
+ public static Double getValue(Measure measure, Double defaultValue) {
+ if (MeasureUtils.hasValue(measure)) {
+ return measure.getValue();
+ }
+ return defaultValue;
+ }
+
+ /**
+ * Tests if a measure has a value
+ *
+ * @param measure the measure
+ * @return whether the measure has a value
+ */
+ public static boolean hasValue(Measure measure) {
+ return measure != null && measure.getValue() != null;
+ }
+
+ /**
+ * Tests if a measure has a data field
+ *
+ * @param measure the measure
+ * @return whether the measure has a data field
+ */
+ public static boolean hasData(Measure measure) {
+ return measure != null && StringUtils.isNotBlank(measure.getData());
+ }
+
+ /**
+ * Sums a series of measures
+ *
+ * @param zeroIfNone whether to return 0 or null in case measures is null
+ * @param measures the series of measures
+ * @return the sum of the measure series
+ */
+ public static Double sum(boolean zeroIfNone, Collection<Measure> measures) {
+ if (measures != null) {
+ return sum(zeroIfNone, measures.toArray(new Measure[measures.size()]));
+ }
+ return zeroIfNone(zeroIfNone);
+ }
+
+ /**
+ * Sums a series of measures
+ *
+ * @param zeroIfNone whether to return 0 or null in case measures is null
+ * @param measures the series of measures
+ * @return the sum of the measure series
+ */
+ public static Double sum(boolean zeroIfNone, Measure... measures) {
+ if (measures == null) {
+ return zeroIfNone(zeroIfNone);
+ }
+ Double sum = 0d;
+ boolean hasValue = false;
+ for (Measure measure : measures) {
+ if (measure != null && measure.getValue() != null) {
+ hasValue = true;
+ sum += measure.getValue();
+ }
+ }
+
+ if (hasValue) {
+ return sum;
+ }
+ return zeroIfNone(zeroIfNone);
+ }
+
+ private static Double zeroIfNone(boolean zeroIfNone) {
+ return zeroIfNone ? 0d : null;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilter.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilter.java
new file mode 100644
index 00000000000..ef406abd1c5
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilter.java
@@ -0,0 +1,31 @@
+/*
+ * 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.measures;
+
+import java.util.Collection;
+
+/**
+ * @since 1.10
+ */
+public interface MeasuresFilter<M> {
+
+ M filter(Collection<Measure> measures);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java
new file mode 100644
index 00000000000..487e67000ad
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/MeasuresFilters.java
@@ -0,0 +1,252 @@
+/*
+ * 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.measures;
+
+import org.sonar.api.qualitymodel.Characteristic;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RulePriority;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @since 1.10
+ */
+public final class MeasuresFilters {
+
+ private MeasuresFilters() {
+ }
+
+ public static MeasuresFilter<Collection<Measure>> all() {
+ return new MeasuresFilter<Collection<Measure>>() {
+ public Collection<Measure> filter(Collection<Measure> measures) {
+ return measures;
+ }
+ };
+ }
+
+ public static MeasuresFilter<Measure> metric(final Metric metric) {
+ return new MetricFilter<Measure>(metric) {
+
+ public Measure filter(Collection<Measure> measures) {
+ if (measures == null) {
+ return null;
+ }
+ for (Measure measure : measures) {
+ if (measure.getClass().equals(Measure.class) &&
+ measure.getMetric().equals(metric) &&
+ measure.getCharacteristic()==null) {
+ return measure;
+ }
+ }
+ return null;
+ }
+ };
+ }
+
+ public static MeasuresFilter<Measure> characteristic(final Metric metric, final Characteristic characteristic) {
+ return new MetricFilter<Measure>(metric) {
+
+ public Measure filter(Collection<Measure> measures) {
+ if (measures == null) {
+ return null;
+ }
+ for (Measure measure : measures) {
+ if (measure.getClass().equals(Measure.class) &&
+ measure.getMetric().equals(metric) &&
+ measure.getCharacteristic()!=null &&
+ measure.getCharacteristic().equals(characteristic)) {
+ return measure;
+ }
+ }
+ return null;
+ }
+ };
+ }
+
+ /**
+ * @since 2.0
+ */
+ public static MeasuresFilter<Measure> measure(final Measure measure) {
+ return new MeasuresFilter<Measure>() {
+ public Measure filter(Collection<Measure> measures) {
+ if (measures==null) {
+ return null;
+ }
+ for (Measure m : measures) {
+ if (m.equals(measure)) {
+ return m;
+ }
+ }
+ return null;
+ }
+ };
+ }
+
+ public static MeasuresFilter<RuleMeasure> rulePriority(final Metric metric, final RulePriority priority) {
+ return new RulePriorityFilter(metric, priority);
+ }
+
+ public static MeasuresFilter<RuleMeasure> ruleCategory(final Metric metric, final Integer category) {
+ return new RuleCategoryFilter(metric, category);
+ }
+
+ public static MeasuresFilter<RuleMeasure> rule(final Metric metric, final Rule rule) {
+ return new RuleFilter(metric, rule);
+ }
+
+ public static MeasuresFilter<Collection<RuleMeasure>> ruleCategories(final Metric metric) {
+ return new MetricFilter<Collection<RuleMeasure>>(metric) {
+
+ private boolean apply(Measure measure) {
+ return measure instanceof RuleMeasure
+ && metric.equals(measure.getMetric())
+ && ((RuleMeasure) measure).getRule() == null
+ && ((RuleMeasure) measure).getRuleCategory() != null;
+ }
+
+ public Collection<RuleMeasure> filter(Collection<Measure> measures) {
+ List<RuleMeasure> result = new ArrayList<RuleMeasure>();
+ if (measures != null) {
+ for (Measure measure : measures) {
+ if (apply(measure)) {
+ result.add((RuleMeasure) measure);
+ }
+ }
+ }
+ return result;
+ }
+ };
+ }
+
+ public static MeasuresFilter<Collection<RuleMeasure>> rules(final Metric metric) {
+ return new MetricFilter<Collection<RuleMeasure>>(metric) {
+
+ private boolean apply(Measure measure) {
+ return measure instanceof RuleMeasure
+ && metric.equals(measure.getMetric())
+ && ((RuleMeasure) measure).getRule() != null;
+ }
+
+ public Collection<RuleMeasure> filter(Collection<Measure> measures) {
+ if (measures == null) {
+ return null;
+ }
+ List<RuleMeasure> result = new ArrayList<RuleMeasure>();
+ for (Measure measure : measures) {
+ if (apply(measure)) {
+ result.add((RuleMeasure) measure);
+ }
+ }
+ return result;
+ }
+ };
+ }
+
+ /**
+ * Used for internal optimizations.
+ */
+ public static abstract class MetricFilter<M> implements MeasuresFilter<M> {
+ private final Metric metric;
+
+ protected MetricFilter(Metric metric) {
+ this.metric = metric;
+ }
+
+ public Metric filterOnMetric() {
+ return metric;
+ }
+ }
+
+ private abstract static class AbstractRuleMeasureFilter<M> extends MetricFilter<M> {
+ protected AbstractRuleMeasureFilter(Metric metric) {
+ super(metric);
+ }
+
+ private boolean apply(Measure measure) {
+ return measure instanceof RuleMeasure
+ && filterOnMetric().equals(measure.getMetric())
+ && doApply((RuleMeasure) measure);
+ }
+
+ abstract boolean doApply(RuleMeasure ruleMeasure);
+
+ public M filter(Collection<Measure> measures) {
+ if (measures == null) {
+ return null;
+ }
+ for (Measure measure : measures) {
+ if (apply(measure)) {
+ return (M) measure;
+ }
+ }
+ return null;
+ }
+ }
+
+ private static class RulePriorityFilter extends AbstractRuleMeasureFilter<RuleMeasure> {
+ private RulePriority priority;
+
+ protected RulePriorityFilter(Metric metric, RulePriority priority) {
+ super(metric);
+ this.priority = priority;
+ }
+
+ @Override
+ boolean doApply(RuleMeasure measure) {
+ return measure.getRule() == null
+ && measure.getRuleCategory() == null
+ && priority.equals(measure.getRulePriority());
+ }
+ }
+
+ private static class RuleCategoryFilter extends AbstractRuleMeasureFilter<RuleMeasure> {
+ private Integer categ;
+
+ protected RuleCategoryFilter(Metric metric, Integer categ) {
+ super(metric);
+ this.categ = categ;
+ }
+
+ @Override
+ boolean doApply(RuleMeasure measure) {
+ return measure.getRule() == null
+ && categ.equals(measure.getRuleCategory())
+ && measure.getRulePriority() == null;
+ }
+ }
+
+
+ private static class RuleFilter extends AbstractRuleMeasureFilter<RuleMeasure> {
+ private Rule rule;
+
+ protected RuleFilter(Metric metric, Rule rule) {
+ super(metric);
+ this.rule = rule;
+ }
+
+ @Override
+ boolean doApply(RuleMeasure measure) {
+ return measure.getRule() != null
+ && rule.equals(measure.getRule());
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java
new file mode 100644
index 00000000000..27dcf62e07a
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metric.java
@@ -0,0 +1,534 @@
+/*
+ * 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.measures;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.BatchExtension;
+import org.sonar.api.ServerExtension;
+import org.sonar.api.database.BaseIdentifiable;
+
+import javax.persistence.*;
+
+/**
+ * @since 1.10
+ */
+@Table(name = "metrics")
+@Entity(name = "Metric")
+public class Metric extends BaseIdentifiable implements ServerExtension, BatchExtension {
+
+ /**
+ * A metric bigger value means a degradation
+ */
+ public final static int DIRECTION_WORST = -1;
+ /**
+ * A metric bigger value means an improvement
+ */
+ public final static int DIRECTION_BETTER = 1;
+ /**
+ * The metric direction has no meaning
+ */
+ public final static int DIRECTION_NONE = 0;
+
+ public enum ValueType {
+ INT, FLOAT, PERCENT, BOOL, STRING, MILLISEC, DATA, LEVEL, DISTRIB
+ }
+
+ public enum Level {
+ OK("Green"), WARN("Orange"), ERROR("Red");
+
+ private String colorName;
+
+ Level(String colorName) {
+ this.colorName = colorName;
+ }
+
+ public String getColorName() {
+ return colorName;
+ }
+ }
+
+ public enum Origin {
+ JAV, GUI, WS
+ }
+
+ @Transient
+ private Formula formula;
+
+ @Column(name = "name", updatable = false, nullable = false, length = 64)
+ private String key;
+
+ @Column(name = "description", updatable = true, nullable = true, length = 255)
+ private String description;
+
+ @Column(name = "val_type", updatable = true, nullable = true)
+ @Enumerated(EnumType.STRING)
+ private ValueType type;
+
+ @Column(name = "direction", updatable = true, nullable = true)
+ private Integer direction;
+
+ @Column(name = "domain", updatable = true, nullable = true, length = 60)
+ private String domain;
+
+ @Column(name = "short_name", updatable = true, nullable = true, length = 64)
+ private String name;
+
+ @Column(name = "qualitative", updatable = true, nullable = true)
+ private Boolean qualitative = Boolean.FALSE;
+
+ @Column(name = "user_managed", updatable = true, nullable = true)
+ private Boolean userManaged = Boolean.FALSE;
+
+ @Column(name = "enabled", updatable = true, nullable = true)
+ private Boolean enabled = Boolean.TRUE;
+
+ @Column(name = "origin", updatable = true, nullable = true, length = 3)
+ @Enumerated(EnumType.STRING)
+ private Origin origin;
+
+ @Column(name = "worst_value", updatable = true, nullable = true, precision = 30, scale = 20)
+ private Double worstValue;
+
+ @Column(name = "best_value", updatable = true, nullable = true, precision = 30, scale = 20)
+ private Double bestValue;
+
+ @Column(name = "optimized_best_value", updatable = true, nullable = true)
+ private Boolean optimizedBestValue;
+
+ @Column(name = "hidden", updatable = true, nullable = true)
+ private Boolean hidden = Boolean.FALSE;
+
+
+ /**
+ * Creates an empty metric
+ */
+ @Deprecated public Metric() {
+ }
+
+ /**
+ * Creates a metric based on its key. Shortcut to Metric(key, ValueType.INT)
+ *
+ * @param key the metric key
+ */
+ public Metric(String key) {
+ this(key, ValueType.INT);
+ }
+
+ /**
+ * Creates a metric based on a key and a type. Shortcut to
+ * Metric(key, key, key, type, -1, Boolean.FALSE, null, false)
+ *
+ * @param key the key
+ * @param type the type
+ */
+ public Metric(String key, ValueType type) {
+ this(key, key, key, type, -1, Boolean.FALSE, null, false);
+ }
+
+ public Metric(String key, String name, String description, ValueType type, Integer direction, Boolean qualitative, String domain) {
+ this(key, name, description, type, direction, qualitative, domain, false);
+ }
+
+ /**
+ * Creates a fully qualified metric. This defaults some values:
+ * <ul>
+ * <li>origin : Origin.JAV</li>
+ * </ul>
+ *
+ * @param key the metric key
+ * @param name the metric name
+ * @param description the metric description
+ * @param type the metric type
+ * @param direction the metric direction
+ * @param qualitative whether the metric is qualitative
+ * @param domain the metric domain
+ * @param userManaged whether the metric is user managed
+ */
+ @Deprecated
+ public Metric(String key, String name, String description, ValueType type, Integer direction, Boolean qualitative, String domain, boolean userManaged) {
+ this.key = key;
+ this.description = description;
+ this.type = type;
+ this.direction = direction;
+ this.domain = domain;
+ this.name = name;
+ this.qualitative = qualitative;
+ this.userManaged = userManaged;
+ this.origin = Origin.JAV;
+ if (ValueType.PERCENT.equals(this.type)) {
+ this.bestValue = (direction==DIRECTION_BETTER ? 100.0 : 0.0);
+ this.worstValue = (direction==DIRECTION_BETTER ? 0.0 : 100.0);
+ }
+ }
+
+ /**
+ * Creates a fully qualified metric. This defaults some values:
+ * <ul>
+ * <li>origin : Origin.JAV</li>
+ * <li>enabled : true</li>
+ * <li>userManaged : true</li>
+ * </ul>
+ *
+ * @param key the metric key
+ * @param name the metric name
+ * @param type the metric type
+ * @param direction the metric direction
+ * @param qualitative whether the metric is qualitative
+ * @param domain the metric domain
+ * @param formula the metric formula
+ */
+ public Metric(String key, String name, ValueType type, Integer direction, Boolean qualitative, String domain, Formula formula) {
+ this.key = key;
+ this.name = name;
+ this.type = type;
+ this.direction = direction;
+ this.domain = domain;
+ this.qualitative = qualitative;
+ this.origin = Origin.JAV;
+ this.enabled = true;
+ this.userManaged = false;
+ this.formula = formula;
+ if (ValueType.PERCENT.equals(this.type)) {
+ this.bestValue = (direction==DIRECTION_BETTER ? 100.0 : 0.0);
+ this.worstValue = (direction==DIRECTION_BETTER ? 0.0 : 100.0);
+ }
+ }
+
+ /**
+ * @return the metric formula
+ */
+ public Formula getFormula() {
+ return formula;
+ }
+
+ /**
+ * Sets the metric formula
+ *
+ * @param formula the formula
+ * @return this
+ */
+ public Metric setFormula(Formula formula) {
+ this.formula = formula;
+ return this;
+ }
+
+ /**
+ * @return wether the metric is qualitative
+ */
+ public Boolean getQualitative() {
+ return qualitative;
+ }
+
+ /**
+ * Sets whether the metric is qualitative
+ *
+ * @param qualitative whether the metric is qualitative
+ * @return this
+ */
+ public Metric setQualitative(Boolean qualitative) {
+ this.qualitative = qualitative;
+ return this;
+ }
+
+ /**
+ * @return the metric key
+ */
+ public String getKey() {
+ return key;
+ }
+
+ /**
+ * Sets the metric key
+ *
+ * @param key the key
+ * @return this
+ */
+ public Metric setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ /**
+ * @return the metric type
+ */
+ public ValueType getType() {
+ return type;
+ }
+
+ /**
+ * Sets the metric type
+ *
+ * @param type the type
+ * @return this
+ */
+ public Metric setType(ValueType type) {
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * @return the metric description
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Sets the metric description
+ *
+ * @param description the description
+ * @return this
+ */
+ public Metric setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ /**
+ * @return whether the metric is a managed by the users (manual metric)
+ */
+ public Boolean getUserManaged() {
+ return userManaged;
+ }
+
+ /**
+ * Sets whether the metric is user managed
+ *
+ * @param userManaged whether the metric is user managed
+ * @return this
+ */
+ public Metric setUserManaged(Boolean userManaged) {
+ this.userManaged = userManaged;
+ return this;
+ }
+
+ /**
+ * @return whether the metric is enabled
+ */
+ public Boolean getEnabled() {
+ return enabled;
+ }
+
+ /**
+ * Sets whether the metric is enabled
+ *
+ * @param enabled whether the metric is enabled
+ * @return this
+ */
+ public Metric setEnabled(Boolean enabled) {
+ this.enabled = enabled;
+ return this;
+ }
+
+ /**
+ * @return the metric direction
+ */
+ public Integer getDirection() {
+ return direction;
+ }
+
+ /**
+ * Sets the metric direction.
+ *
+ * @param direction the direction
+ */
+ public Metric setDirection(Integer direction) {
+ this.direction = direction;
+ return this;
+ }
+
+ /**
+ * @return the domain of the metric
+ */
+ public String getDomain() {
+ return domain;
+ }
+
+ /**
+ * Sets the domain for the metric (General, Complexity...)
+ *
+ * @param domain the domain
+ * @return this
+ */
+ public Metric setDomain(String domain) {
+ this.domain = domain;
+ return this;
+ }
+
+ /**
+ * @return the metric name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the metric name
+ *
+ * @param name the name
+ * @return this
+ */
+ public Metric setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ /**
+ * @return the origin of the metric - Internal use only
+ */
+ public Origin getOrigin() {
+ return origin;
+ }
+
+ /**
+ * Set the origin of the metric - Internal use only
+ *
+ * @param origin the origin
+ * @return this
+ */
+ public Metric setOrigin(Origin origin) {
+ this.origin = origin;
+ return this;
+ }
+
+ public Double getWorstValue() {
+ return worstValue;
+ }
+
+ public Double getBestValue() {
+ return bestValue;
+ }
+
+ /**
+ * @return this
+ */
+ public Metric setWorstValue(Double d) {
+ this.worstValue = d;
+ return this;
+ }
+
+ /**
+ * @param bestValue the best value. It can be null.
+ * @return this
+ */
+ public Metric setBestValue(Double bestValue) {
+ this.bestValue = bestValue;
+ return this;
+ }
+
+ /**
+ * @return whether the metric is of a numeric type (int, percentage...)
+ */
+ public boolean isNumericType() {
+ return ValueType.INT.equals(type)
+ || ValueType.FLOAT.equals(type)
+ || ValueType.PERCENT.equals(type)
+ || ValueType.BOOL.equals(type)
+ || ValueType.MILLISEC.equals(type);
+ }
+
+ /**
+ * @return whether the metric is of type data
+ */
+ public boolean isDataType() {
+ return ValueType.DATA.equals(type) || ValueType.DISTRIB.equals(type);
+ }
+
+ /**
+ * @return whether the metric is of type percentage
+ */
+ public boolean isPercentageType() {
+ return ValueType.PERCENT.equals(type);
+ }
+
+ public Metric setOptimizedBestValue(Boolean b) {
+ this.optimizedBestValue = b;
+ return this;
+ }
+
+ public Boolean isOptimizedBestValue() {
+ return optimizedBestValue;
+ }
+
+ public Boolean isHidden() {
+ return hidden;
+ }
+
+ public Metric setHidden(Boolean hidden) {
+ this.hidden = hidden;
+ return this;
+ }
+
+ @Override
+ public int hashCode() {
+ return key.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Metric)) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ Metric other = (Metric) obj;
+ return key.equals(other.getKey());
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("key", key)
+ .append("name", name)
+ .append("type", type)
+ .append("enabled", enabled)
+ .append("qualitative", qualitative)
+ .append("direction", direction)
+ .append("domain", domain)
+ .append("worstValue", worstValue)
+ .append("bestValue", bestValue)
+ .append("optimizedBestValue", optimizedBestValue)
+ .append("hidden", hidden)
+ .toString();
+ }
+
+ /**
+ * Merge with fields from other metric. All fields are copied, except the id.
+ * @return this
+ */
+ public Metric merge(final Metric with) {
+ this.description = with.description;
+ this.domain = with.domain;
+ this.enabled = with.enabled;
+ this.qualitative = with.qualitative;
+ this.worstValue = with.worstValue;
+ this.bestValue = with.bestValue;
+ this.optimizedBestValue = with.optimizedBestValue;
+ this.direction = with.direction;
+ this.key = with.key;
+ this.type = with.type;
+ this.name = with.name;
+ this.userManaged = with.userManaged;
+ this.origin = with.origin;
+ this.hidden = with.hidden;
+ return this;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metrics.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metrics.java
new file mode 100644
index 00000000000..c733e59a637
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/Metrics.java
@@ -0,0 +1,32 @@
+/*
+ * 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.measures;
+
+import org.sonar.api.BatchExtension;
+import org.sonar.api.ServerExtension;
+
+import java.util.List;
+
+/**
+ * @since 1.10
+ */
+public interface Metrics extends BatchExtension, ServerExtension {
+ List<Metric> getMetrics();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/PersistenceMode.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/PersistenceMode.java
new file mode 100644
index 00000000000..47404215eb0
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/PersistenceMode.java
@@ -0,0 +1,42 @@
+/*
+ * 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.measures;
+
+/**
+ * Mode of persistence for resources and measures. Usage :
+ * <ul>
+ * <li>MEMORY : temporary data used only in batch for calculations.</li>
+ * <li>DATABASE : measures with big data (several kB), like details of code coverage hits</li>
+ * <li>FULL : both MEMORY and DATABASE (default).</li>
+ * </ul>
+ *
+ * @since 1.10
+ */
+public enum PersistenceMode {
+ MEMORY, DATABASE, FULL;
+
+ public boolean useMemory() {
+ return equals(MEMORY) || equals(FULL);
+ }
+
+ public boolean useDatabase() {
+ return equals(DATABASE) || equals(FULL);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/PropertiesBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/PropertiesBuilder.java
new file mode 100644
index 00000000000..620b8646287
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/PropertiesBuilder.java
@@ -0,0 +1,83 @@
+/*
+ * 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.measures;
+
+import org.sonar.api.utils.KeyValueFormat;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * @since 1.10
+ */
+public class PropertiesBuilder<KEY, VALUE> {
+ private Metric metric;
+ private Map<KEY, VALUE> props;
+
+ public PropertiesBuilder(Metric metric, Map<KEY, VALUE> map) {
+ this.props = new TreeMap<KEY, VALUE>(map);
+ this.metric = metric;
+ }
+
+ public PropertiesBuilder(Metric metric) {
+ this.props = new TreeMap<KEY, VALUE>();
+ this.metric = metric;
+ }
+
+ public PropertiesBuilder() {
+ this.props = new TreeMap<KEY, VALUE>();
+ }
+
+ public PropertiesBuilder<KEY, VALUE> clear() {
+ this.props.clear();
+ return this;
+ }
+
+ public Map<KEY, VALUE> getProps() {
+ return props;
+ }
+
+ public Metric getMetric() {
+ return metric;
+ }
+
+ public PropertiesBuilder<KEY, VALUE> setMetric(Metric metric) {
+ this.metric = metric;
+ return this;
+ }
+
+ public PropertiesBuilder<KEY, VALUE> add(KEY key, VALUE value) {
+ props.put(key, value);
+ return this;
+ }
+
+ public PropertiesBuilder<KEY, VALUE> addAll(Map<KEY, VALUE> map) {
+ props.putAll(map);
+ return this;
+ }
+
+ public Measure build() {
+ return new Measure(metric, buildData());
+ }
+
+ public String buildData() {
+ return KeyValueFormat.format(props);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/RangeDistributionBuilder.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/RangeDistributionBuilder.java
new file mode 100644
index 00000000000..c9e148ffaf1
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/RangeDistributionBuilder.java
@@ -0,0 +1,249 @@
+/*
+ * 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.measures;
+
+import org.apache.commons.collections.SortedBag;
+import org.apache.commons.collections.Transformer;
+import org.apache.commons.collections.bag.TransformedSortedBag;
+import org.apache.commons.collections.bag.TreeBag;
+import org.apache.commons.lang.NumberUtils;
+import org.sonar.api.utils.KeyValueFormat;
+import org.sonar.api.utils.SonarException;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Utility to build a distribution based on defined ranges
+ * <p/>
+ * <p>An example of usage : you wish to record the percentage of lines of code that belong to method
+ * with pre-defined ranges of complexity.</p>
+ *
+ * @since 1.10
+ */
+public class RangeDistributionBuilder implements MeasureBuilder {
+
+ private Metric metric;
+ private SortedBag countBag;
+ private boolean isEmpty = true;
+ private Number[] bottomLimits;
+ private boolean isValid = true;
+
+ /**
+ * RangeDistributionBuilder for a metric and a defined range
+ * Each entry is initialized at zero
+ *
+ * @param metric the metric to record the measure against
+ * @param bottomLimits the bottom limits of ranges to be used
+ */
+ public RangeDistributionBuilder(Metric metric, Number[] bottomLimits) {
+ setMetric(metric);
+ init(bottomLimits);
+ }
+
+ private void init(Number[] bottomLimits) {
+ this.bottomLimits = new Number[bottomLimits.length];
+ System.arraycopy(bottomLimits, 0, this.bottomLimits, 0, this.bottomLimits.length);
+ Arrays.sort(this.bottomLimits);
+ changeDoublesToInts();
+ countBag = TransformedSortedBag.decorate(new TreeBag(), new RangeTransformer());
+ doClear();
+ }
+
+ private void changeDoublesToInts() {
+ boolean onlyInts = true;
+ for (Number bottomLimit : bottomLimits) {
+ if (NumberUtils.compare(bottomLimit.intValue(), bottomLimit.doubleValue())!=0) {
+ onlyInts=false;
+ }
+ }
+ if (onlyInts) {
+ for (int i=0 ; i<bottomLimits.length ; i++) {
+ bottomLimits[i] = new Integer(bottomLimits[i].intValue());
+ }
+ }
+ }
+
+ public RangeDistributionBuilder(Metric metric) {
+ this.metric = metric;
+ }
+
+ /**
+ * Gives the bottom limits of ranges used
+ *
+ * @return the bottom limits of defined range for the distribution
+ */
+ public Number[] getBottomLimits() {
+ return bottomLimits;
+ }
+
+ /**
+ * Increments an entry by 1
+ *
+ * @param value the value to use to pick the entry to increment
+ * @return the current object
+ */
+ public RangeDistributionBuilder add(Number value) {
+ return add(value, 1);
+ }
+
+ /**
+ * Increments an entry
+ *
+ * @param value the value to use to pick the entry to increment
+ * @param count the number by which to increment
+ * @return the current object
+ */
+ public RangeDistributionBuilder add(Number value, int count) {
+ if (value != null && greaterOrEqualsThan(value, bottomLimits[0])) {
+ this.countBag.add(value, count);
+ isEmpty = false;
+ }
+ return this;
+ }
+
+ private RangeDistributionBuilder addLimitCount(Number limit, int count) {
+ for (Number bottomLimit : bottomLimits) {
+ if (NumberUtils.compare(bottomLimit.doubleValue(), limit.doubleValue()) == 0) {
+ this.countBag.add(limit, count);
+ isEmpty = false;
+ return this;
+ }
+ }
+ isValid = false;
+ return this;
+ }
+
+ /**
+ * Adds an existing Distribution to the current one.
+ * It will create the entries if they don't exist.
+ * Can be used to add the values of children resources for example
+ * <p/>
+ * Since 2.2, the distribution returned will be invalidated in case the
+ * measure given does not use the same bottom limits
+ *
+ * @param measure the measure to add to the current one
+ * @return the current object
+ */
+ public RangeDistributionBuilder add(Measure measure) {
+ if (measure != null && measure.getData() != null) {
+ Map<Double, Double> map = KeyValueFormat.parse(measure.getData(), new KeyValueFormat.DoubleNumbersPairTransformer());
+ Number[] limits = map.keySet().toArray(new Number[map.size()]);
+ if (bottomLimits == null) {
+ init(limits);
+
+ } else if (!areSameLimits(bottomLimits, map.keySet())) {
+ isValid = false;
+ }
+
+ if (isValid) {
+ for (Map.Entry<Double, Double> entry : map.entrySet()) {
+ addLimitCount(entry.getKey(), entry.getValue().intValue());
+ }
+ }
+ }
+ return this;
+ }
+
+ private boolean areSameLimits(Number[] bottomLimits, Set<Double> limits) {
+ if (limits.size() == bottomLimits.length) {
+ for (Number l : bottomLimits) {
+ if (!limits.contains(l.doubleValue())) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Resets all entries to zero
+ *
+ * @return the current object
+ */
+ public RangeDistributionBuilder clear() {
+ doClear();
+ return this;
+ }
+
+ private void doClear() {
+ if (countBag != null) {
+ countBag.clear();
+ }
+ if (bottomLimits != null) {
+ countBag.addAll(Arrays.asList(bottomLimits));
+ }
+ isEmpty = true;
+ }
+
+ /**
+ * @return whether the current object is empty or not
+ */
+ public boolean isEmpty() {
+ return isEmpty;
+ }
+
+ /**
+ * Shortcut for <code>build(true)</code>
+ *
+ * @return the built measure
+ */
+ public Measure build() {
+ return build(true);
+ }
+
+ /**
+ * Used to build a measure from the current object
+ *
+ * @param allowEmptyData should be built if current object is empty
+ * @return the built measure
+ */
+ public Measure build(boolean allowEmptyData) {
+ if (isValid && (!isEmpty || allowEmptyData)) {
+ return new Measure(metric, KeyValueFormat.format(countBag, -1));
+ }
+ return null;
+ }
+
+ private class RangeTransformer implements Transformer {
+ public Object transform(Object o) {
+ Number n = (Number) o;
+ for (int i = bottomLimits.length - 1; i >= 0; i--) {
+ if (greaterOrEqualsThan(n, bottomLimits[i])) {
+ return bottomLimits[i];
+ }
+ }
+ return null;
+ }
+ }
+
+ private static boolean greaterOrEqualsThan(Number n1, Number n2) {
+ return NumberUtils.compare(n1.doubleValue(), n2.doubleValue()) >= 0;
+ }
+
+ private void setMetric(Metric metric) {
+ if (metric == null || !metric.isDataType()) {
+ throw new SonarException("Metric is null or has unvalid type");
+ }
+ this.metric = metric;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java
new file mode 100644
index 00000000000..6ed4ab82a83
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/RuleMeasure.java
@@ -0,0 +1,132 @@
+/*
+ * 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.measures;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RulePriority;
+
+/**
+ * @since 1.10
+ */
+public class RuleMeasure extends Measure {
+
+ private Rule rule;
+ private RulePriority rulePriority;
+ private Integer ruleCategory;
+
+ /**
+ * This constructor is for internal use only. Please use static methods createForXXX().
+ */
+ public RuleMeasure(Metric metric, Rule rule, RulePriority rulePriority, Integer ruleCategory) {
+ super(metric);
+ this.rule = rule;
+ this.rulePriority = rulePriority;
+ this.ruleCategory = ruleCategory;
+ }
+
+ public Rule getRule() {
+ return rule;
+ }
+
+ public void setRule(Rule rule) {
+ this.rule = rule;
+ }
+
+ public RulePriority getRulePriority() {
+ return rulePriority;
+ }
+
+ public void setRulePriority(RulePriority rulePriority) {
+ this.rulePriority = rulePriority;
+ }
+
+ public Integer getRuleCategory() {
+ return ruleCategory;
+ }
+
+ public void setRuleCategory(Integer ruleCategory) {
+ this.ruleCategory = ruleCategory;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj.getClass().equals(RuleMeasure.class))) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ RuleMeasure other = (RuleMeasure) obj;
+ return new EqualsBuilder()
+ .append(getMetric(), other.getMetric())
+ .append(rule, other.rule)
+ .append(rulePriority, other.rulePriority)
+ .append(ruleCategory, other.ruleCategory)
+ .isEquals();
+ }
+
+ @Override
+ public RuleMeasure setValue(Double v) {
+ return (RuleMeasure) super.setValue(v);
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(17, 37).
+ append(getMetric()).
+ append(rule).
+ append(rulePriority).
+ append(ruleCategory).
+ toHashCode();
+
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).
+ append("id", getId()).
+ append("metric", metric).
+ append("value", value).
+ append("data", data).
+ append("description", description).
+ append("alertStatus", alertStatus).
+ append("alertText", alertText).
+ append("tendency", tendency).
+ append("rule", rule).
+ append("category", ruleCategory).
+ append("priority", rulePriority).
+ toString();
+ }
+
+ public static RuleMeasure createForRule(Metric metric, Rule rule, Double value) {
+ return (RuleMeasure) new RuleMeasure(metric, rule, null, null).setValue(value);
+ }
+
+ public static RuleMeasure createForPriority(Metric metric, RulePriority priority, Double value) {
+ return (RuleMeasure) new RuleMeasure(metric, null, priority, null).setValue(value);
+ }
+
+ public static RuleMeasure createForCategory(Metric metric, Integer category, Double value) {
+ return (RuleMeasure) new RuleMeasure(metric, null, null, category).setValue(value);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/SumChildDistributionFormula.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/SumChildDistributionFormula.java
new file mode 100644
index 00000000000..423d2df83e9
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/SumChildDistributionFormula.java
@@ -0,0 +1,50 @@
+/*
+ * 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.measures;
+
+import java.util.List;
+import java.util.Collections;
+import java.util.Collection;
+
+/**
+ * @since 2.0
+ *
+ * Used to consolidate a distribution measure throughout the resource tree
+ */
+public class SumChildDistributionFormula implements Formula {
+
+ public List<Metric> dependsUponMetrics() {
+ return Collections.emptyList();
+ }
+
+ public Measure calculate(FormulaData data, FormulaContext context) {
+ Collection<Measure> measures = data.getChildrenMeasures(context.getTargetMetric());
+ if (measures == null || measures.isEmpty()) {
+ return null;
+ }
+ else {
+ RangeDistributionBuilder distribution = new RangeDistributionBuilder(context.getTargetMetric());
+ for (Measure measure : measures) {
+ distribution.add(measure);
+ }
+ return distribution.build();
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/SumChildValuesFormula.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/SumChildValuesFormula.java
new file mode 100644
index 00000000000..e6b271ccaf0
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/SumChildValuesFormula.java
@@ -0,0 +1,47 @@
+/*
+ * 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.measures;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @since 1.11
+ */
+public class SumChildValuesFormula implements Formula {
+
+ private boolean saveZeroIfNoChildValues;
+
+ public SumChildValuesFormula(boolean saveZeroIfNoChildValues) {
+ this.saveZeroIfNoChildValues = saveZeroIfNoChildValues;
+ }
+
+ public List<Metric> dependsUponMetrics() {
+ return Collections.emptyList();
+ }
+
+ public Measure calculate(FormulaData data, FormulaContext context) {
+ Double sum = MeasureUtils.sum(saveZeroIfNoChildValues, data.getChildrenMeasures(context.getTargetMetric()));
+ if (sum != null) {
+ return new Measure(context.getTargetMetric(), sum);
+ }
+ return null;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/measures/WeightedMeanAggregationFormula.java b/sonar-plugin-api/src/main/java/org/sonar/api/measures/WeightedMeanAggregationFormula.java
new file mode 100644
index 00000000000..219679255a1
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/measures/WeightedMeanAggregationFormula.java
@@ -0,0 +1,68 @@
+/*
+ * 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.measures;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * @since 2.0
+ */
+public class WeightedMeanAggregationFormula implements Formula {
+
+ private Metric weightingMetric;
+ private boolean zeroIfNoValues=false;
+
+ public WeightedMeanAggregationFormula(Metric weightingMetric, boolean zeroIfNoValues) {
+ this.weightingMetric = weightingMetric;
+ if (weightingMetric==null) {
+ throw new IllegalArgumentException("Metric can not be null");
+ }
+ this.zeroIfNoValues = zeroIfNoValues;
+ }
+
+ public List<Metric> dependsUponMetrics() {
+ return Collections.emptyList();
+ }
+
+ public Measure calculate(FormulaData data, FormulaContext context) {
+ double sum=0.0;
+ double count=0.0;
+ boolean hasValue=false;
+
+ for (FormulaData child : data.getChildren()) {
+ Measure measure = child.getMeasure(context.getTargetMetric());
+ Measure weightingMeasure = child.getMeasure(weightingMetric);
+ if (MeasureUtils.haveValues(measure, weightingMeasure)) {
+ sum += (measure.getValue() * weightingMeasure.getValue());
+ count += weightingMeasure.getValue();
+ hasValue=true;
+ }
+ }
+
+ if (!hasValue && !zeroIfNoValues) {
+ return null;
+ }
+
+ double result = (count==0.0 ? 0.0 : sum/count);
+ return new Measure(context.getTargetMetric(), result);
+ }
+}
+
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/platform/Environment.java b/sonar-plugin-api/src/main/java/org/sonar/api/platform/Environment.java
new file mode 100644
index 00000000000..b9dd664f72c
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/platform/Environment.java
@@ -0,0 +1,50 @@
+/*
+ * 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.platform;
+
+import org.sonar.api.BatchComponent;
+import org.sonar.api.ServerComponent;
+
+/**
+ * @since 2.2
+ */
+public enum Environment implements BatchComponent, ServerComponent {
+
+ /*
+ * When will GRADLE, ANT, ECLIPSE, INTELLIJ_IDEA be added to this list ? :-)
+ */
+ SERVER, MAVEN3, MAVEN2;
+
+ public boolean isServer() {
+ return this==SERVER;
+ }
+
+ public boolean isMaven2Batch() {
+ return this==MAVEN2;
+ }
+
+ public boolean isMaven3Batch() {
+ return this==MAVEN3;
+ }
+
+ public boolean isBatch() {
+ return isMaven2Batch() || isMaven3Batch();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/platform/PluginRepository.java b/sonar-plugin-api/src/main/java/org/sonar/api/platform/PluginRepository.java
new file mode 100644
index 00000000000..611c3eeca28
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/platform/PluginRepository.java
@@ -0,0 +1,113 @@
+/*
+ * 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.platform;
+
+import org.picocontainer.Characteristics;
+import org.picocontainer.MutablePicoContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.*;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * @since 2.2
+ */
+public abstract class PluginRepository implements BatchComponent, ServerComponent {
+
+ private static final Logger LOG = LoggerFactory.getLogger(PluginRepository.class);
+
+ private Map<String, Plugin> pluginByKey = new HashMap<String, Plugin>();
+ private Map<Object, Plugin> pluginByExtension = new IdentityHashMap<Object, Plugin>();
+
+ public void registerPlugin(MutablePicoContainer container, Plugin plugin, Class<? extends Extension> extensionClass) {
+ LOG.debug("Registering the plugin {}", plugin.getKey());
+ pluginByKey.put(plugin.getKey(), plugin);
+ for (Object extension : plugin.getExtensions()) {
+ if (isExtension(extension, extensionClass)) {
+ LOG.debug("Registering the extension: {}", extension);
+ container.as(Characteristics.CACHE).addComponent(getExtensionKey(extension), extension);
+ pluginByExtension.put(extension, plugin);
+ }
+ }
+ }
+
+ public Collection<Plugin> getPlugins() {
+ return pluginByKey.values();
+ }
+
+ public Plugin getPlugin(String key) {
+ return pluginByKey.get(key);
+ }
+
+ /**
+ * Returns the list of properties of a plugin
+ */
+ public Property[] getProperties(Plugin plugin) {
+ if (plugin != null) {
+ Class<? extends Plugin> classInstance = plugin.getClass();
+ if (classInstance.isAnnotationPresent(Properties.class)) {
+ return classInstance.getAnnotation(Properties.class).value();
+ }
+ }
+ return new Property[0];
+ }
+
+ public Property[] getProperties(String pluginKey) {
+ return getProperties(pluginByKey.get(pluginKey));
+ }
+
+ public Plugin getPluginForExtension(Object extension) {
+ Plugin plugin = pluginByExtension.get(extension);
+ if (plugin == null && !(extension instanceof Class)) {
+ plugin = pluginByExtension.get(extension.getClass());
+ }
+ return plugin;
+ }
+
+ public String getPluginKeyForExtension(Object extension) {
+ Plugin plugin = getPluginForExtension(extension);
+ if (plugin != null) {
+ return plugin.getKey();
+ }
+ return null;
+ }
+
+ private boolean isExtension(Object extension, Class<? extends Extension> extensionClass) {
+ Class clazz = (extension instanceof Class ? (Class) extension : extension.getClass());
+ return extensionClass.isAssignableFrom(clazz);
+ }
+
+ public void registerExtension(MutablePicoContainer container, Plugin plugin, Object extension) {
+ container.as(Characteristics.CACHE).addComponent(getExtensionKey(extension), extension);
+ pluginByExtension.put(extension, plugin);
+ }
+
+ protected Object getExtensionKey(Object component) {
+ if (component instanceof Class) {
+ return component;
+ }
+ return component.getClass().getCanonicalName() + "-" + component.toString();
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/platform/Server.java b/sonar-plugin-api/src/main/java/org/sonar/api/platform/Server.java
new file mode 100644
index 00000000000..1a5bbc35ca7
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/platform/Server.java
@@ -0,0 +1,37 @@
+/*
+ * 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.platform;
+
+import org.sonar.api.BatchComponent;
+import org.sonar.api.ServerComponent;
+
+import java.util.Date;
+
+/**
+ * @since 2.2
+ */
+public abstract class Server implements BatchComponent, ServerComponent {
+
+ public abstract String getId();
+
+ public abstract String getVersion();
+
+ public abstract Date getStartedAt();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/platform/ServerFileSystem.java b/sonar-plugin-api/src/main/java/org/sonar/api/platform/ServerFileSystem.java
new file mode 100644
index 00000000000..11f2015c1e6
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/platform/ServerFileSystem.java
@@ -0,0 +1,39 @@
+/*
+ * 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.platform;
+
+import org.sonar.api.ServerComponent;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * @since 2.3
+ */
+public interface ServerFileSystem extends ServerComponent {
+
+ File getHomeDir();
+
+ /**
+ * @param suffixes the file suffixes. If null, then return all the files, whatever their suffix
+ */
+ List<File> getExtensions(String dirName, String... suffixes);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/platform/ServerStartHandler.java b/sonar-plugin-api/src/main/java/org/sonar/api/platform/ServerStartHandler.java
new file mode 100644
index 00000000000..741ebc698dd
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/platform/ServerStartHandler.java
@@ -0,0 +1,31 @@
+/*
+ * 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.platform;
+
+import org.sonar.api.ServerExtension;
+
+/**
+ * @since 2.2
+ */
+public interface ServerStartHandler extends ServerExtension {
+
+ public void onServerStart(Server server);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/platform/ServerStopHandler.java b/sonar-plugin-api/src/main/java/org/sonar/api/platform/ServerStopHandler.java
new file mode 100644
index 00000000000..30edbc9fbe8
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/platform/ServerStopHandler.java
@@ -0,0 +1,31 @@
+/*
+ * 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.platform;
+
+import org.sonar.api.ServerExtension;
+
+/**
+ * @since 2.2
+ */
+public interface ServerStopHandler extends ServerExtension {
+
+ public void onServerStop(Server server);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/Alert.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/Alert.java
new file mode 100644
index 00000000000..843dfecd236
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/Alert.java
@@ -0,0 +1,211 @@
+/*
+ * 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.profiles;
+
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+import org.sonar.api.database.BaseIdentifiable;
+import org.sonar.api.measures.Metric;
+
+import javax.persistence.*;
+
+/**
+ * Class to map alerts with hibernate model
+ */
+@Entity
+@Table(name = "alerts")
+public class Alert extends BaseIdentifiable implements Cloneable {
+ /**
+ * Operator strictly greater than
+ */
+ public static final String OPERATOR_GREATER = ">";
+
+ /**
+ * Operator strictly lesser than
+ */
+ public static final String OPERATOR_SMALLER = "<";
+
+ /**
+ * Operator equals
+ */
+ public static final String OPERATOR_EQUALS = "=";
+
+ /**
+ * Operator not equals
+ */
+ public static final String OPERATOR_NOT_EQUALS = "!=";
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "profile_id")
+ @Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
+ private RulesProfile rulesProfile;
+
+ @ManyToOne(fetch = FetchType.EAGER)
+ @JoinColumn(name = "metric_id", nullable = true)
+ @Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
+ private Metric metric;
+
+ @Column(name = "operator", updatable = false, nullable = true, length = 3)
+ private String operator;
+
+ @Column(name = "value_error", updatable = false, nullable = true, length = 64)
+ private String valueError;
+
+ @Column(name = "value_warning", updatable = false, nullable = true, length = 64)
+ private String valueWarning;
+
+ /**
+ * Default constructor
+ */
+ public Alert() {
+ }
+
+ /**
+ * Creates an alert
+ *
+ * @param rulesProfile the profile used to trigger the alert
+ * @param metric the metric tested for the alert
+ * @param operator the operator defined
+ * @param valueError the error value
+ * @param valueWarning the warning value
+ */
+ public Alert(RulesProfile rulesProfile, Metric metric, String operator, String valueError, String valueWarning) {
+ super();
+ this.rulesProfile = rulesProfile;
+ this.metric = metric;
+ this.operator = operator;
+ this.valueError = valueError;
+ this.valueWarning = valueWarning;
+ }
+
+ /**
+ * @return the alert profile
+ */
+ public RulesProfile getRulesProfile() {
+ return rulesProfile;
+ }
+
+ /**
+ * Sets the alert profile
+ */
+ public void setRulesProfile(RulesProfile rulesProfile) {
+ this.rulesProfile = rulesProfile;
+ }
+
+ /**
+ * @return the alert metric
+ */
+ public Metric getMetric() {
+ return metric;
+ }
+
+ /**
+ * Sets the alert metric
+ */
+ public void setMetric(Metric metric) {
+ this.metric = metric;
+ }
+
+ /**
+ * @return the alert operator
+ */
+ public String getOperator() {
+ return operator;
+ }
+
+ /**
+ * Sets the alert operator
+ */
+ public void setOperator(String operator) {
+ this.operator = operator;
+ }
+
+ /**
+ * @return the error value
+ */
+ public String getValueError() {
+ return valueError;
+ }
+
+ /**
+ * Sets the error value if any
+ */
+ public void setValueError(String valueError) {
+ this.valueError = valueError;
+ }
+
+ /**
+ * @return the warning value
+ */
+ public String getValueWarning() {
+ return valueWarning;
+ }
+
+ /**
+ * Sets the warning value if any
+ */
+ public void setValueWarning(String valueWarning) {
+ this.valueWarning = valueWarning;
+ }
+
+ /**
+ * @return whether the operator is greater than
+ */
+ public boolean isGreaterOperator() {
+ return operator.equals(OPERATOR_GREATER);
+ }
+
+ /**
+ * @return whether the operator is lesser than
+ */
+ public boolean isSmallerOperator() {
+ return operator.equals(OPERATOR_SMALLER);
+ }
+
+ /**
+ * @return whether the operator is equals
+ */
+ public boolean isEqualsOperator() {
+ return operator.equals(OPERATOR_EQUALS);
+ }
+
+ /**
+ * @return whether the operator is not equals
+ */
+ public boolean isNotEqualsOperator() {
+ return operator.equals(OPERATOR_NOT_EQUALS);
+ }
+
+
+ public String getAlertLabel(Metric.Level level) {
+ return new StringBuilder()
+ .append(getMetric().getName())
+ .append(" ").append(getOperator())
+ .append(" ")
+ .append(level.equals(Metric.Level.ERROR) ? getValueError() : getValueWarning()).toString();
+ }
+
+ @Override
+ public Object clone() {
+ return new Alert(getRulesProfile(), getMetric(), getOperator(), getValueError(), getValueWarning());
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/AnnotationProfileImporter.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/AnnotationProfileImporter.java
new file mode 100644
index 00000000000..83eec7a46a2
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/AnnotationProfileImporter.java
@@ -0,0 +1,69 @@
+/*
+ * 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.profiles;
+
+import org.sonar.api.rules.RulePriority;
+import org.sonar.api.utils.ValidationMessages;
+import org.sonar.check.AnnotationIntrospector;
+import org.sonar.check.BelongsToProfile;
+
+import java.io.Reader;
+import java.util.Collection;
+
+/**
+ * @since 2.3
+ */
+public final class AnnotationProfileImporter extends ProfileImporter {
+
+ private String repositoryKey;
+ private Collection<Class> annotatedClasses;
+
+ AnnotationProfileImporter(String repositoryKey, Collection<Class> annotatedClasses) {
+ this.repositoryKey = repositoryKey;
+ this.annotatedClasses = annotatedClasses;
+ }
+
+ public static AnnotationProfileImporter create(String repositoryKey, Collection<Class> annotatedClasses) {
+ return new AnnotationProfileImporter(repositoryKey, annotatedClasses);
+ }
+
+ @Override
+ public ProfilePrototype importProfile(Reader reader, ValidationMessages messages) {
+ ProfilePrototype profile = ProfilePrototype.create();
+ if (annotatedClasses != null) {
+ for (Class aClass : annotatedClasses) {
+ BelongsToProfile belongsToProfile = (BelongsToProfile) aClass.getAnnotation(BelongsToProfile.class);
+ registerRule(aClass, belongsToProfile, profile);
+ }
+ }
+
+ return profile;
+ }
+
+ private void registerRule(Class aClass, BelongsToProfile belongsToProfile, ProfilePrototype profile) {
+ if (belongsToProfile != null) {
+ RulePriority priority = null;
+ if (belongsToProfile.priority() != null) {
+ priority = RulePriority.fromCheckPriority(belongsToProfile.priority());
+ }
+ profile.activateRule(repositoryKey, AnnotationIntrospector.getCheckKey(aClass), priority);
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfileDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfileDefinition.java
new file mode 100644
index 00000000000..fb945bde59f
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfileDefinition.java
@@ -0,0 +1,65 @@
+/*
+ * 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.profiles;
+
+import org.sonar.api.ServerExtension;
+
+/**
+ * @since 2.3
+ */
+public abstract class ProfileDefinition implements ServerExtension {
+
+ private String name;
+ private String language;
+
+ protected ProfileDefinition() {
+ }
+
+ protected ProfileDefinition(String name, String language) {
+ this.name = name;
+ this.language = language;
+ }
+
+ public final String getName() {
+ return name;
+ }
+
+ public final ProfileDefinition setName(String s) {
+ this.name = s;
+ return this;
+ }
+
+ public final String getLanguage() {
+ return language;
+ }
+
+ public final ProfileDefinition setLanguage(String s) {
+ this.language = s;
+ return this;
+ }
+
+ public abstract ProfilePrototype createPrototype();
+
+
+ @Override
+ public final String toString() {
+ return new StringBuilder().append("[name=").append(name).append(",language=").append(language).append("]").toString();
+ }
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfileExporter.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfileExporter.java
new file mode 100644
index 00000000000..e439a4635ae
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfileExporter.java
@@ -0,0 +1,46 @@
+/*
+ * 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.profiles;
+
+import org.sonar.api.BatchExtension;
+import org.sonar.api.ServerExtension;
+
+import java.io.Writer;
+
+/**
+ * @since 2.3
+ */
+public abstract class ProfileExporter implements BatchExtension, ServerExtension {
+
+ private String[] supportedRepositories = new String[0];
+
+ public abstract void exportProfile(RulesProfile profile, Writer writer);
+
+ protected final void setSupportedRepositories(String... repositoryKeys) {
+ supportedRepositories = (repositoryKeys != null ? repositoryKeys : new String[0]);
+ }
+
+ /**
+ * @return if empty, then all repositories are supported.
+ */
+ public final String[] getSupportedRepositories() {
+ return supportedRepositories;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfileImporter.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfileImporter.java
new file mode 100644
index 00000000000..3df8020f539
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfileImporter.java
@@ -0,0 +1,46 @@
+/*
+ * 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.profiles;
+
+import org.sonar.api.ServerExtension;
+import org.sonar.api.utils.ValidationMessages;
+
+import java.io.Reader;
+
+/**
+ * @since 2.3
+ */
+public abstract class ProfileImporter implements ServerExtension {
+
+ private String[] supportedRepositories = new String[0];
+
+ public abstract ProfilePrototype importProfile(Reader reader, ValidationMessages messages);
+
+ protected final void setSupportedRepositories(String... repositoryKeys) {
+ supportedRepositories = (repositoryKeys != null ? repositoryKeys : new String[0]);
+ }
+
+ /**
+ * @return if empty, then all repositories are supported.
+ */
+ public final String[] getSupportedRepositories() {
+ return supportedRepositories;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfilePrototype.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfilePrototype.java
new file mode 100644
index 00000000000..b98d1b84b36
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/ProfilePrototype.java
@@ -0,0 +1,149 @@
+/*
+ * 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.profiles;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.rules.RulePriority;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * BETA
+ * @since 2.3
+ */
+public final class ProfilePrototype {
+
+ private List<RulePrototype> rules = new ArrayList<RulePrototype>();
+
+ private ProfilePrototype() {
+ }
+
+ public static ProfilePrototype create() {
+ return new ProfilePrototype();
+ }
+
+ public List<RulePrototype> getRules() {
+ return rules;
+ }
+
+ public List<RulePrototype> getRulesByRepositoryKey(String repositoryKey) {
+ List<RulePrototype> result = new ArrayList<RulePrototype>();
+ for (RulePrototype rule : rules) {
+ if (StringUtils.equals(repositoryKey, rule.getRepositoryKey())) {
+ result.add(rule);
+ }
+ }
+ return result;
+ }
+
+ public RulePrototype getRule(String repositoryKey, String key) {
+ for (RulePrototype rule : rules) {
+ if (StringUtils.equals(repositoryKey, rule.getRepositoryKey()) && StringUtils.equals(key, rule.getKey())) {
+ return rule;
+ }
+ }
+ return null;
+ }
+
+ /**
+ *
+ * @param repositoryKey
+ * @param key
+ * @param nullablePriority if null, then the default rule priority is used.
+ * @return the created rule
+ */
+ public RulePrototype activateRule(String repositoryKey, String key, RulePriority nullablePriority) {
+ RulePrototype rule = RulePrototype.create(repositoryKey, key);
+ rule.setPriority(nullablePriority);
+ rules.add(rule);
+ return rule;
+ }
+
+ public RulePrototype activateRule(RulePrototype rule) {
+ rules.add(rule);
+ return rule;
+ }
+
+ public static final class RulePrototype {
+ private String repositoryKey;
+ private String key;
+ private RulePriority priority = null;
+ private Map<String, String> parameters = new HashMap<String, String>();
+
+ private RulePrototype() {
+ }
+
+ public static RulePrototype create() {
+ return new RulePrototype();
+ }
+
+ public static RulePrototype create(String repositoryKey, String key) {
+ return new RulePrototype().setRepositoryKey(repositoryKey).setKey(key);
+ }
+
+ public String getRepositoryKey() {
+ return repositoryKey;
+ }
+
+ public RulePrototype setRepositoryKey(String s) {
+ this.repositoryKey = s;
+ return this;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public RulePrototype setKey(String s) {
+ this.key = s;
+ return this;
+ }
+
+ public RulePriority getPriority() {
+ return priority;
+ }
+
+ public RulePrototype setPriority(RulePriority p) {
+ this.priority = p;
+ return this;
+ }
+
+ public RulePrototype setParameter(String key, String value) {
+ parameters.put(key, value);
+ return this;
+ }
+
+ public Map<String, String> getParameters() {
+ return parameters;
+ }
+
+ public String getParameter(String key) {
+ return parameters.get(key);
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder().append("[repository=").append(repositoryKey).append(",key=").append(key).append("]").toString();
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/RulesProfile.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/RulesProfile.java
new file mode 100644
index 00000000000..01be1e63567
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/RulesProfile.java
@@ -0,0 +1,350 @@
+/*
+ * 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.profiles;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.Transformer;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.sonar.api.database.model.ResourceModel;
+import org.sonar.api.rules.ActiveRule;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RulePriority;
+
+import javax.persistence.*;
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+@Table(name = "rules_profiles")
+public class RulesProfile implements Cloneable {
+
+ /**
+ * The profile key for the embedded profile Sonar Way
+ */
+ public static final String SONAR_WAY_NAME = "Sonar way";
+
+ /**
+ * The profile key for the embedded profile Sonar Way with Findbugs
+ */
+ public static final String SONAR_WAY_FINDBUGS_NAME = "Sonar way with Findbugs";
+
+ /**
+ * The profile key for the embedded profile Sun checks
+ */
+ public static final String SUN_CONVENTIONS_NAME = "Sun checks";
+
+ @Id
+ @Column(name = "id")
+ @GeneratedValue
+ private Integer id;
+
+ @Column(name = "name", updatable = true, nullable = false)
+ private String name;
+
+ @Column(name = "default_profile", updatable = true, nullable = false)
+ private Boolean defaultProfile = Boolean.FALSE;
+
+ @Column(name = "provided", updatable = true, nullable = false)
+ private Boolean provided = Boolean.FALSE;
+
+ @Column(name = "language", updatable = true, nullable = false)
+ private String language;
+
+ @OneToMany(mappedBy = "rulesProfile", fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE})
+ private List<ActiveRule> activeRules = new ArrayList<ActiveRule>();
+
+ @OneToMany(mappedBy = "rulesProfile", fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE})
+ private List<Alert> alerts = new ArrayList<Alert>();
+
+ @OneToMany(mappedBy = "rulesProfile", fetch = FetchType.LAZY)
+ private List<ResourceModel> projects = new ArrayList<ResourceModel>();
+
+ /**
+ * @deprecated use the factory method create()
+ */
+ @Deprecated
+ public RulesProfile() {
+ }
+
+ /**
+ * <p>Creates a profile of rules with empty active rules, empty alerts and empty project lists.</p>
+ *
+ * @param name the name to be used to access the profile, will be used as a key and display name
+ * @param language the language to which this profile applies
+ * @deprecated use the factory method create()
+ */
+ @Deprecated
+ public RulesProfile(String name, String language) {
+ this.name = name;
+ this.language = language;
+ this.activeRules = new ArrayList<ActiveRule>();
+ this.alerts = new ArrayList<Alert>();
+ this.projects = new ArrayList<ResourceModel>();
+ }
+
+ /**
+ * <p>Creates a profile of rules with empty active rules, empty alerts and empty project lists.</p>
+ *
+ * @param name the name to be used to access the profile, will be used as a key and display name
+ * @param language the language to which this profile applies
+ * @param defaultProfile whether this is the default profile for the language
+ * @param provided whether the profile is embarked in core Sonar
+ */
+ @Deprecated
+ public RulesProfile(String name, String language, boolean defaultProfile, boolean provided) {
+ this(name, language);
+ this.defaultProfile = defaultProfile;
+ this.provided = provided;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ /**
+ * @return the name of the profile
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the name of the profile
+ */
+ public RulesProfile setName(String s) {
+ this.name = s;
+ return this;
+ }
+
+ /**
+ * @return the list of active rules
+ */
+ public List<ActiveRule> getActiveRules() {
+ return activeRules;
+ }
+
+ /**
+ * Sets the list of active rules
+ */
+ public void setActiveRules(List<ActiveRule> activeRules) {
+ this.activeRules = activeRules;
+ }
+
+ /**
+ * @return whether this is the default profile for the language
+ */
+ public Boolean getDefaultProfile() {
+ return defaultProfile;
+ }
+
+ /**
+ * Sets whether this is the default profile for the language
+ */
+ public void setDefaultProfile(Boolean b) {
+ this.defaultProfile = b;
+ }
+
+ /**
+ * @return whether the profile ships with Sonar core
+ */
+ public Boolean getProvided() {
+ return provided;
+ }
+
+ /**
+ * Sets wether the profile ships with Sonar core
+ */
+ public void setProvided(Boolean provided) {
+ this.provided = provided;
+ }
+
+ /**
+ * @return the language of the profile
+ */
+ public String getLanguage() {
+ return language;
+ }
+
+ /**
+ * Sets the language for the profile
+ */
+ public RulesProfile setLanguage(String s) {
+ this.language = s;
+ return this;
+ }
+
+ /**
+ * @return the list of alerts defined in the profile
+ */
+ public List<Alert> getAlerts() {
+ return alerts;
+ }
+
+ /**
+ * Sets the list of alerts for the profile
+ */
+ public void setAlerts(List<Alert> alerts) {
+ this.alerts = alerts;
+ }
+
+ /**
+ * @return the list of projects attached to the profile
+ */
+ public List<ResourceModel> getProjects() {
+ return projects;
+ }
+
+ /**
+ * Sets the list of projects attached to the profile
+ */
+ public void setProjects(List<ResourceModel> projects) {
+ this.projects = projects;
+ }
+
+ /**
+ * @return the list of active rules for a given priority
+ */
+ public List<ActiveRule> getActiveRules(RulePriority priority) {
+ List<ActiveRule> result = new ArrayList<ActiveRule>();
+ for (ActiveRule activeRule : getActiveRules()) {
+ if (activeRule.getPriority().equals(priority)) {
+ result.add(activeRule);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @deprecated use getActiveRulesByRepository()
+ */
+ @Deprecated
+ public List<ActiveRule> getActiveRulesByPlugin(String repositoryKey) {
+ return getActiveRulesByRepository(repositoryKey);
+ }
+
+ public List<ActiveRule> getActiveRulesByRepository(String repositoryKey) {
+ List<ActiveRule> result = new ArrayList<ActiveRule>();
+ for (ActiveRule activeRule : getActiveRules()) {
+ if (repositoryKey.equals(activeRule.getPluginName())) {
+ result.add(activeRule);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return an active rule from a plugin key and a rule key if the rule is activated, null otherwise
+ */
+ public ActiveRule getActiveRule(String repositoryKey, String ruleKey) {
+ for (ActiveRule activeRule : getActiveRules()) {
+ if (StringUtils.equals(activeRule.getRepositoryKey(), repositoryKey) && StringUtils.equals(activeRule.getRuleKey(), ruleKey)) {
+ return activeRule;
+ }
+ }
+ return null;
+ }
+
+ public ActiveRule getActiveRuleByConfigKey(String repositoryKey, String configKey) {
+ for (ActiveRule activeRule : getActiveRules()) {
+ if (StringUtils.equals(activeRule.getRepositoryKey(), repositoryKey) && StringUtils.equals(activeRule.getConfigKey(), configKey)) {
+ return activeRule;
+ }
+ }
+ return null;
+ }
+
+ public ActiveRule getActiveRule(Rule rule) {
+ return getActiveRule(rule.getRepositoryKey(), rule.getKey());
+ }
+
+ /**
+ *
+ * @param rule
+ * @param optionalPriority if null, then the default rule priority is used
+ * @return
+ */
+ public ActiveRule activateRule(Rule rule, RulePriority optionalPriority) {
+ ActiveRule activeRule = new ActiveRule();
+ activeRule.setRule(rule);
+ activeRule.setRulesProfile(this);
+ activeRule.setPriority(optionalPriority==null ? rule.getPriority() : optionalPriority);
+ activeRules.add(activeRule);
+ return activeRule;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof RulesProfile)) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ RulesProfile other = (RulesProfile) obj;
+ return new EqualsBuilder().append(language, other.getLanguage()).append(name, other.getName()).isEquals();
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(17, 37).append(language).append(name).toHashCode();
+ }
+
+ @Override
+ public Object clone() {
+ RulesProfile clone = new RulesProfile(getName(), getLanguage(), getDefaultProfile(), getProvided());
+ if (CollectionUtils.isNotEmpty(getActiveRules())) {
+ clone.setActiveRules(new ArrayList<ActiveRule>(CollectionUtils.collect(getActiveRules(), new Transformer() {
+ public Object transform(Object input) {
+ return ((ActiveRule) input).clone();
+ }
+ })));
+ }
+ if (CollectionUtils.isNotEmpty(getAlerts())) {
+ clone.setAlerts(new ArrayList<Alert>(CollectionUtils.collect(getAlerts(), new Transformer() {
+ public Object transform(Object input) {
+ return ((Alert) input).clone();
+ }
+ })));
+ }
+ if (CollectionUtils.isNotEmpty(getProjects())) {
+ clone.setProjects(new ArrayList<ResourceModel>(CollectionUtils.collect(getProjects(), new Transformer() {
+ public Object transform(Object input) {
+ return ((ResourceModel) input).clone();
+ }
+ })));
+ }
+ return clone;
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder().append("[name=").append(name).append(",language=").append(language).append("]").toString();
+ }
+
+ public static RulesProfile create(String name, String language) {
+ return new RulesProfile().setName(name).setLanguage(language);
+ }
+
+ public static RulesProfile create() {
+ return new RulesProfile();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileDefinition.java
new file mode 100644
index 00000000000..fec2423c0f9
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileDefinition.java
@@ -0,0 +1,66 @@
+/*
+ * 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.profiles;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.CharEncoding;
+import org.sonar.api.utils.SonarException;
+import org.sonar.api.utils.ValidationMessages;
+
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.List;
+
+/**
+ * @since 2.3
+ */
+public abstract class XMLProfileDefinition extends ProfileDefinition {
+
+ private ClassLoader classloader;
+ private String xmlClassPath;
+
+ protected XMLProfileDefinition(String name, String language, ClassLoader classloader, String xmlClassPath) {
+ super(name, language);
+ this.classloader = classloader;
+ this.xmlClassPath = xmlClassPath;
+ }
+
+ @Override
+ public final ProfilePrototype createPrototype() {
+ Reader reader = new InputStreamReader(classloader.getResourceAsStream(xmlClassPath), Charset.forName(CharEncoding.UTF_8));
+ try {
+ ValidationMessages validation = ValidationMessages.create();
+ ProfilePrototype profile = XMLProfileImporter.create().importProfile(reader, validation);
+ if (validation.hasErrors()) {
+ // TODO do not loose messages
+ throw new SonarException("Fail to parse the file: " + xmlClassPath);
+ }
+ return profile;
+
+ } finally {
+ IOUtils.closeQuietly(reader);
+ }
+ }
+
+// public static XMLProfileDefinition create(String name, String language, ClassLoader classloader, String xmlClassloaderPath) {
+// return new XMLProfileDefinition(name, language).setClassloader(classloader).setXmlClassPath(xmlClassloaderPath);
+// }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileExporter.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileExporter.java
new file mode 100644
index 00000000000..de76bd3aba6
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileExporter.java
@@ -0,0 +1,111 @@
+/*
+ * 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.profiles;
+
+import org.apache.commons.lang.StringEscapeUtils;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.rules.ActiveRule;
+import org.sonar.api.rules.ActiveRuleParam;
+import org.sonar.api.utils.SonarException;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * @since 2.3
+ */
+public final class XMLProfileExporter extends ProfileExporter {
+
+ private XMLProfileExporter() {
+ // support all repositories
+ }
+
+ public static XMLProfileExporter create() {
+ return new XMLProfileExporter();
+ }
+
+ @Override
+ public void exportProfile(RulesProfile profile, Writer writer) {
+ try {
+ appendHeader(writer);
+ appendRules(profile, writer);
+ appendFooter(writer);
+
+ } catch (IOException e) {
+ throw new SonarException("Fail to export the profile " + profile, e);
+ }
+ }
+
+ private void appendRules(RulesProfile profile, Writer writer) throws IOException {
+ if (!profile.getActiveRules().isEmpty()) {
+ writer.append("<rules>");
+ for (ActiveRule activeRule : profile.getActiveRules()) {
+ appendRule(activeRule, writer);
+ }
+ writer.append("</rules>");
+ }
+ }
+
+ private void appendHeader(Writer writer) throws IOException {
+ writer.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+ + "<!-- Generated by Sonar -->"
+ + "<profile>");
+ }
+
+ private void appendRule(ActiveRule activeRule, Writer writer) throws IOException {
+ writer.append("<rule><repositoryKey>");
+ writer.append(activeRule.getRepositoryKey());
+ writer.append("</repositoryKey><key>");
+ StringEscapeUtils.escapeXml(writer, activeRule.getRuleKey());
+ writer.append("</key>");
+ if (activeRule.getPriority() != null) {
+ writer.append("<priority>");
+ writer.append(activeRule.getPriority().name());
+ writer.append("</priority>");
+ }
+ appendRuleParameters(activeRule, writer);
+ writer.append("</rule>");
+ }
+
+ private void appendRuleParameters(ActiveRule activeRule, Writer writer) throws IOException {
+ if (activeRule.getActiveRuleParams() != null && !activeRule.getActiveRuleParams().isEmpty()) {
+ writer.append("<parameters>");
+ for (ActiveRuleParam activeRuleParam : activeRule.getActiveRuleParams()) {
+ appendRuleParameter(writer, activeRuleParam);
+ }
+ writer.append("</parameters>");
+ }
+ }
+
+ private void appendRuleParameter(Writer writer, ActiveRuleParam activeRuleParam) throws IOException {
+ if (StringUtils.isNotBlank(activeRuleParam.getValue())) {
+ writer.append("<parameter><key>");
+ StringEscapeUtils.escapeXml(writer, activeRuleParam.getKey());
+ writer.append("</key><value>");
+ StringEscapeUtils.escapeXml(writer, activeRuleParam.getValue());
+ writer.append("</value>");
+ writer.append("</parameter>");
+ }
+ }
+
+ private void appendFooter(Writer writer) throws IOException {
+ writer.append("</profile>");
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileImporter.java b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileImporter.java
new file mode 100644
index 00000000000..d0b9c4be653
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/profiles/XMLProfileImporter.java
@@ -0,0 +1,125 @@
+/*
+ * 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.profiles;
+
+import org.apache.commons.lang.StringUtils;
+import org.codehaus.stax2.XMLInputFactory2;
+import org.codehaus.staxmate.SMInputFactory;
+import org.codehaus.staxmate.in.SMHierarchicCursor;
+import org.codehaus.staxmate.in.SMInputCursor;
+import org.sonar.api.rules.RulePriority;
+import org.sonar.api.utils.ValidationMessages;
+
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+import java.io.Reader;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @since 2.3
+ */
+public final class XMLProfileImporter extends ProfileImporter {
+
+ private XMLProfileImporter() {
+ // support all repositories
+ }
+
+ public static XMLProfileImporter create() {
+ return new XMLProfileImporter();
+ }
+
+ @Override
+ public ProfilePrototype importProfile(Reader reader, ValidationMessages messages) {
+ ProfilePrototype profile = ProfilePrototype.create();
+
+ XMLInputFactory xmlFactory = XMLInputFactory2.newInstance();
+ xmlFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
+ xmlFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE);
+ // just so it won't try to load DTD in if there's DOCTYPE
+ xmlFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
+ xmlFactory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE);
+
+ SMInputFactory inputFactory = new SMInputFactory(xmlFactory);
+ try {
+ SMHierarchicCursor rootC = inputFactory.rootElementCursor(reader);
+ rootC.advance(); // <profile>
+ SMInputCursor cursor = rootC.childElementCursor();
+ while (cursor.getNext() != null) {
+ String nodeName = cursor.getLocalName();
+ if (StringUtils.equals("rules", nodeName)) {
+ SMInputCursor rulesCursor = cursor.childElementCursor("rule");
+ processRules(rulesCursor, profile);
+ }
+ }
+ } catch (XMLStreamException e) {
+ messages.addError("unvalidXml", "XML is not valid: " + e.getMessage());
+ }
+ return profile;
+ }
+
+ private void processRules(SMInputCursor rulesCursor, ProfilePrototype profile) throws XMLStreamException {
+ while (rulesCursor.getNext() != null) {
+ SMInputCursor ruleCursor = rulesCursor.childElementCursor();
+ ProfilePrototype.RulePrototype rule = ProfilePrototype.RulePrototype.create();
+ profile.activateRule(rule);
+
+ while (ruleCursor.getNext() != null) {
+ String nodeName = ruleCursor.getLocalName();
+
+ if (StringUtils.equals("repositoryKey", nodeName)) {
+ rule.setRepositoryKey(StringUtils.trim(ruleCursor.collectDescendantText(false)));
+
+ } else if (StringUtils.equals("key", nodeName)) {
+ rule.setKey(StringUtils.trim(ruleCursor.collectDescendantText(false)));
+
+ } else if (StringUtils.equals("priority", nodeName)) {
+ rule.setPriority(RulePriority.valueOf(StringUtils.trim(ruleCursor.collectDescendantText(false))));
+
+ } else if (StringUtils.equals("parameters", nodeName)) {
+ SMInputCursor propsCursor = ruleCursor.childElementCursor("parameter");
+ processParameters(propsCursor, rule);
+ }
+ }
+ }
+ }
+
+ private void processParameters(SMInputCursor propsCursor, ProfilePrototype.RulePrototype rule) throws XMLStreamException {
+ while (propsCursor.getNext() != null) {
+ SMInputCursor propCursor = propsCursor.childElementCursor();
+ String key = null;
+ String value = null;
+ while (propCursor.getNext() != null) {
+ String nodeName = propCursor.getLocalName();
+ if (StringUtils.equals("key", nodeName)) {
+ key = StringUtils.trim(propCursor.collectDescendantText(false));
+
+ } else if (StringUtils.equals("value", nodeName)) {
+ value = StringUtils.trim(propCursor.collectDescendantText(false));
+ }
+ }
+ if (key != null) {
+ rule.setParameter(key, value);
+ }
+ }
+ }
+
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/qualitymodel/Characteristic.java b/sonar-plugin-api/src/main/java/org/sonar/api/qualitymodel/Characteristic.java
new file mode 100644
index 00000000000..22bf78e2b3b
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/qualitymodel/Characteristic.java
@@ -0,0 +1,255 @@
+/*
+ * 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.qualitymodel;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+import org.hibernate.annotations.Sort;
+import org.hibernate.annotations.SortType;
+import org.sonar.api.rules.Rule;
+
+import java.util.*;
+import javax.persistence.*;
+
+/**
+ * @since 2.3
+ */
+@Entity
+@Table(name = "characteristics")
+public final class Characteristic implements Comparable<Characteristic> {
+
+ public static final int ROOT_DEPTH = 1;
+
+ @Id
+ @Column(name = "id")
+ @GeneratedValue
+ private Integer id;
+
+ @Column(name = "kee", nullable = true, length = 100)
+ private String key;
+
+ @Column(name = "name", nullable = true, length = 100)
+ private String name;
+
+ @Column(name = "depth")
+ private int depth=ROOT_DEPTH;
+
+ @Column(name = "characteristic_order")
+ private int order=0;
+
+ @ManyToOne(fetch = FetchType.EAGER)
+ @JoinColumn(name = "quality_model_id")
+ private Model model;
+
+ @ManyToOne(fetch = FetchType.EAGER)
+ @JoinColumn(name = "rule_id")
+ private Rule rule;
+
+
+ @ManyToMany
+ @JoinTable(
+ name = "characteristic_edges",
+ joinColumns = @JoinColumn(name = "child_id", referencedColumnName = "id"),
+ inverseJoinColumns = @JoinColumn(name = "parent_id",
+ referencedColumnName = "id")
+ )
+ private List<Characteristic> parents = new ArrayList<Characteristic>();
+
+ @Sort(type = SortType.NATURAL)
+ @ManyToMany(mappedBy = "parents", cascade = CascadeType.ALL)
+ private List<Characteristic> children = new ArrayList<Characteristic>();
+
+ Characteristic() {
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ Characteristic setId(Integer id) {
+ this.id = id;
+ return this;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ Characteristic setKey(String s) {
+ this.key = StringUtils.trimToEmpty(s);
+ return this;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ Characteristic setName(String s, boolean asKey) {
+ this.name = StringUtils.trimToEmpty(s);
+ if (asKey) {
+ this.key = StringUtils.upperCase(this.name);
+ this.key = StringUtils.replaceChars(this.key, ' ', '_');
+ }
+ return this;
+ }
+
+ public Model getModel() {
+ return model;
+ }
+
+ Characteristic setModel(Model model) {
+ this.model = model;
+ return this;
+ }
+
+ public Rule getRule() {
+ return rule;
+ }
+
+ public Characteristic setRule(Rule r) {
+ this.rule = r;
+ return this;
+ }
+
+ public Characteristic addChildren(Characteristic... list) {
+ if (list != null) {
+ for (Characteristic c : list) {
+ addChild(c);
+ }
+ }
+ return this;
+ }
+
+ public Characteristic addChild(Characteristic child) {
+ propagateDepth(child, depth+1);
+ child.addParents(this);
+ child.setModel(model);
+ children.add(child);
+ return this;
+ }
+
+ private static void propagateDepth(Characteristic characteristic, int depth) {
+ characteristic.setDepth(depth);
+ for (Characteristic child : characteristic.getChildren()) {
+ propagateDepth(child, depth+1);
+ }
+ }
+
+ Characteristic addParents(Characteristic... pc) {
+ if (pc!=null) {
+ Collections.addAll(this.parents, pc);
+ }
+ return this;
+ }
+
+ public List<Characteristic> getParents() {
+ return parents;
+ }
+
+ public Characteristic getParent(String name) {
+ for (Characteristic parent : parents) {
+ if (StringUtils.equals(parent.getName(), name)) {
+ return parent;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Children sorted by insertion order
+ */
+ public List<Characteristic> getChildren() {
+ return children;
+ }
+
+ public Characteristic getChild(String name) {
+ for (Characteristic child : children) {
+ if (StringUtils.equals(child.getName(), name)) {
+ return child;
+ }
+ }
+ return null;
+ }
+
+ public int getDepth() {
+ return depth;
+ }
+
+ public boolean isRoot() {
+ return depth==ROOT_DEPTH;
+ }
+
+ Characteristic setDepth(int i) {
+ this.depth = i;
+ return this;
+ }
+
+ public int getOrder() {
+ return order;
+ }
+
+ Characteristic setOrder(int i) {
+ this.order = i;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Characteristic that = (Characteristic) o;
+ if (key != null ? !key.equals(that.key) : that.key != null) {
+ return false;
+ }
+ if (rule != null ? !rule.equals(that.rule) : that.rule != null) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = key != null ? key.hashCode() : 0;
+ result = 31 * result + (rule != null ? rule.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
+ .append("key", key)
+ .append("name", name)
+ .append("rule", rule)
+ .toString();
+ }
+
+ public int compareTo(Characteristic o) {
+ if (equals(o)) {
+ return 0;
+ }
+ return order - o.order;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/qualitymodel/Model.java b/sonar-plugin-api/src/main/java/org/sonar/api/qualitymodel/Model.java
new file mode 100644
index 00000000000..079b4573518
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/qualitymodel/Model.java
@@ -0,0 +1,168 @@
+/*
+ * 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.qualitymodel;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.rules.Rule;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.persistence.*;
+
+/**
+ * @since 2.3
+ */
+@Entity
+@Table(name = "quality_models")
+public final class Model implements Comparable<Model> {
+
+ @Id
+ @Column(name = "id")
+ @GeneratedValue
+ private Integer id;
+
+ @Column(name = "name", nullable = false, unique = true, length = 100)
+ private String name;
+
+ @OneToMany(mappedBy = "model", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
+ private List<Characteristic> characteristics = new ArrayList<Characteristic>();
+
+ Model() {
+ }
+
+ public static Model create() {
+ return new Model();
+ }
+
+ public static Model createByName(String s) {
+ return new Model().setName(s);
+ }
+
+ public Characteristic createCharacteristicByName(String name) {
+ Characteristic c = new Characteristic().setName(name, true);
+ return addCharacteristic(c);
+ }
+
+ public Characteristic createCharacteristicByKey(String key, String name) {
+ Characteristic c = new Characteristic().setKey(key).setName(name, false);
+ return addCharacteristic(c);
+ }
+
+ public Characteristic createCharacteristicByRule(Rule rule) {
+ Characteristic c = new Characteristic().setRule(rule);
+ return addCharacteristic(c);
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ Model setId(Integer id) {
+ this.id = id;
+ return this;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public List<Characteristic> getRootCharacteristics() {
+ return getCharacteristicsByDepth(Characteristic.ROOT_DEPTH);
+ }
+
+ public Model setName(String name) {
+ this.name = StringUtils.trim(name);
+ return this;
+ }
+
+ private Characteristic addCharacteristic(Characteristic c) {
+ c.setModel(this);
+ c.setOrder(characteristics.size()+1);
+ characteristics.add(c);
+ return c;
+ }
+
+ public List<Characteristic> getCharacteristics() {
+ return characteristics;
+ }
+
+ public Characteristic getCharacteristicByKey(String key) {
+ for (Characteristic characteristic : getCharacteristics()) {
+ if (StringUtils.equals(key, characteristic.getKey())) {
+ return characteristic;
+ }
+ }
+ return null;
+ }
+
+ public List<Characteristic> getCharacteristicsByDepth(int depth) {
+ List<Characteristic> result = new ArrayList<Characteristic>();
+ for (Characteristic c : characteristics) {
+ if (c.getDepth()==depth) {
+ result.add(c);
+ }
+ }
+ return result;
+ }
+
+ public Characteristic getCharacteristicByName(String name) {
+ for (Characteristic characteristic : getCharacteristics()) {
+ if (StringUtils.equals(name, characteristic.getName())) {
+ return characteristic;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Model model = (Model) o;
+ if (name != null ? !name.equals(model.name) : model.name != null) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return name != null ? name.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("id", id)
+ .append("name", name)
+ .toString();
+ }
+
+ public int compareTo(Model o) {
+ return getName().compareTo(o.getName());
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/qualitymodel/ModelDefinition.java b/sonar-plugin-api/src/main/java/org/sonar/api/qualitymodel/ModelDefinition.java
new file mode 100644
index 00000000000..5d6980ccf7c
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/qualitymodel/ModelDefinition.java
@@ -0,0 +1,61 @@
+/*
+ * 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.qualitymodel;
+
+import org.sonar.api.ServerExtension;
+
+/**
+ *
+ * This extension point must be implemented to define a new quality model.
+ *
+ * @since 2.3
+ */
+public abstract class ModelDefinition implements ServerExtension {
+
+ private String name;
+
+ public ModelDefinition(String name) {
+ this.name = name;
+ }
+
+ public final String getName() {
+ return name;
+ }
+
+ public abstract Model create();
+
+ @Override
+ public final boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ModelDefinition that = (ModelDefinition) o;
+ return name.equals(that.name);
+ }
+
+ @Override
+ public final int hashCode() {
+ return name.hashCode();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/qualitymodel/ModelProvider.java b/sonar-plugin-api/src/main/java/org/sonar/api/qualitymodel/ModelProvider.java
new file mode 100644
index 00000000000..432ba848fcd
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/qualitymodel/ModelProvider.java
@@ -0,0 +1,42 @@
+/*
+ * 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.qualitymodel;
+
+import org.sonar.api.BatchComponent;
+import org.sonar.api.ServerComponent;
+
+/**
+ * @since 2.3
+ */
+public interface ModelProvider extends BatchComponent, ServerComponent {
+
+ /**
+ * @return null if the name is not found
+ */
+ Model findByName(String name);
+
+ /**
+ * @return null if the name is not found
+ */
+ ModelDefinition findDefinitionByName(String name);
+
+ Model reset(String name);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/AbstractLanguage.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/AbstractLanguage.java
new file mode 100644
index 00000000000..26fe63effe9
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/AbstractLanguage.java
@@ -0,0 +1,93 @@
+/*
+ * 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.resources;
+
+/**
+ * Inherit this class to define a new language like PLSQL, PHP or C#
+ *
+ * @since 1.10
+ */
+public abstract class AbstractLanguage implements Language {
+ private final String key;
+ private String name;
+
+ /**
+ * Better to use AbstractLanguage(key, name). In this case, key and name will be the same
+ */
+ public AbstractLanguage(String key) {
+ this(key, key);
+ }
+
+ /**
+ * Should be the constructor used to build an AbstractLanguage.
+ *
+ * @param key the key that will be used to retrieve the language. This key is important as it will be used to teint rules repositories...
+ * @param name the display name of the language in the interface
+ */
+ public AbstractLanguage(String key, String name) {
+ this.key = key.toLowerCase();
+ this.name = name;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getKey() {
+ return key;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the language name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Language language = (Language) o;
+ return !(key != null ? !key.equals(language.getKey()) : language.getKey() != null);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return (key != null ? key.hashCode() : 0);
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/DefaultProjectFileSystem.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/DefaultProjectFileSystem.java
new file mode 100644
index 00000000000..1c04e67b0a5
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/DefaultProjectFileSystem.java
@@ -0,0 +1,350 @@
+/*
+ * 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.resources;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.filefilter.*;
+import org.apache.commons.lang.CharEncoding;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.batch.maven.MavenUtils;
+import org.sonar.api.utils.SonarException;
+import org.sonar.api.utils.WildcardPattern;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * An implementation of ProjectFileSystem
+ *
+ * @since 1.10
+ */
+public class DefaultProjectFileSystem implements ProjectFileSystem {
+
+ private Project project;
+
+ /**
+ * Creates a DefaultProjectFileSystem based on a project
+ *
+ * @param project
+ */
+ public DefaultProjectFileSystem(Project project) {
+ this.project = project;
+ }
+
+ /**
+ * Source encoding. Never null, it returns the default plateform charset if it is not defined in project.
+ */
+ public Charset getSourceCharset() {
+ return MavenUtils.getSourceCharset(project.getPom());
+ }
+
+
+ /**
+ * Basedir is the project root directory.
+ */
+ public File getBasedir() {
+ return project.getPom().getBasedir();
+ }
+
+ /**
+ * Build directory is by default "target" in maven projects.
+ */
+ public File getBuildDir() {
+ return resolvePath(project.getPom().getBuild().getDirectory());
+ }
+
+ /**
+ * Directory where classes are placed. By default "target/classes" in maven projects.
+ */
+ public File getBuildOutputDir() {
+ return resolvePath(project.getPom().getBuild().getOutputDirectory());
+ }
+
+ /**
+ * The list of directories for sources
+ */
+ public List<File> getSourceDirs() {
+ return resolvePaths(project.getPom().getCompileSourceRoots());
+ }
+
+ /**
+ * Adds a source directory
+ *
+ * @return the current object
+ */
+ public DefaultProjectFileSystem addSourceDir(File dir) {
+ if (dir == null) {
+ throw new IllegalArgumentException("Can not add null to project source dirs");
+ }
+ project.getPom().getCompileSourceRoots().add(0, dir.getAbsolutePath());
+ return this;
+ }
+
+ /**
+ * The list of directories for tests
+ */
+ public List<File> getTestDirs() {
+ return resolvePaths(project.getPom().getTestCompileSourceRoots());
+ }
+
+ /**
+ * Adds a test directory
+ *
+ * @return the current object
+ */
+ public DefaultProjectFileSystem addTestDir(File dir) {
+ if (dir == null) {
+ throw new IllegalArgumentException("Can not add null to project test dirs");
+ }
+ project.getPom().getTestCompileSourceRoots().add(0, dir.getAbsolutePath());
+ return this;
+ }
+
+ /**
+ * @return the directory where reporting is placed. Default is target/sites
+ */
+ public File getReportOutputDir() {
+ return resolvePath(project.getPom().getReporting().getOutputDirectory());
+ }
+
+ /**
+ * @return the Sonar working directory. Default is "target/sonar"
+ */
+ public File getSonarWorkingDirectory() {
+ try {
+ File dir = new File(project.getPom().getBuild().getDirectory(), "sonar");
+ FileUtils.forceMkdir(dir);
+ return dir;
+
+ } catch (IOException e) {
+ throw new SonarException("Unable to retrieve Sonar working directory.", e);
+ }
+ }
+
+ public File resolvePath(String path) {
+ File file = new File(path);
+ if (!file.isAbsolute()) {
+ file = new File(project.getPom().getBasedir(), path);
+ }
+ return file;
+ }
+
+ private List<File> resolvePaths(List<String> paths) {
+ List<File> result = new ArrayList<File>();
+ if (paths != null) {
+ for (String path : paths) {
+ result.add(resolvePath(path));
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Gets the list of source files for given languages
+ *
+ * @param langs language filter. If null or empty, will return empty list
+ */
+ public List<File> getSourceFiles(Language... langs) {
+ return getFiles(getSourceDirs(), true, langs);
+ }
+
+ /**
+ * Gets the list of java source files
+ */
+ public List<File> getJavaSourceFiles() {
+ return getSourceFiles(Java.INSTANCE);
+ }
+
+ /**
+ * @return whether there are java source
+ */
+ public boolean hasJavaSourceFiles() {
+ return !getJavaSourceFiles().isEmpty();
+ }
+
+ /**
+ * Gets the list of test files for given languages
+ *
+ * @param langs language filter. If null or empty, will return empty list
+ */
+ public List<File> getTestFiles(Language... langs) {
+ return getFiles(getTestDirs(), false, langs);
+ }
+
+ /**
+ * @return whether there are tests files
+ */
+ public boolean hasTestFiles(Language lang) {
+ return !getTestFiles(lang).isEmpty();
+ }
+
+ private List<File> getFiles(List<File> directories, boolean applyExclusionPatterns, Language... langs) {
+ List<File> result = new ArrayList<File>();
+ if (directories == null) {
+ return result;
+ }
+
+ IOFileFilter suffixFilter = getFileSuffixFilter(langs);
+ WildcardPattern[] exclusionPatterns = getExclusionPatterns(applyExclusionPatterns);
+
+ for (File dir : directories) {
+ if (dir.exists()) {
+ IOFileFilter exclusionFilter = new ExclusionFilter(dir, exclusionPatterns);
+ IOFileFilter visibleFileFilter = HiddenFileFilter.VISIBLE;
+ AndFileFilter filters = new AndFileFilter(new AndFileFilter(exclusionFilter, suffixFilter), visibleFileFilter);
+ result.addAll(FileUtils.listFiles(dir, filters, HiddenFileFilter.VISIBLE));
+ }
+ }
+ return result;
+ }
+
+ private WildcardPattern[] getExclusionPatterns(boolean applyExclusionPatterns) {
+ WildcardPattern[] exclusionPatterns;
+ if (applyExclusionPatterns) {
+ exclusionPatterns = WildcardPattern.create(project.getExclusionPatterns());
+ } else {
+ exclusionPatterns = new WildcardPattern[0];
+ }
+ return exclusionPatterns;
+ }
+
+ private IOFileFilter getFileSuffixFilter(Language... langs) {
+ IOFileFilter suffixFilter = FileFilterUtils.trueFileFilter();
+ if (langs != null && langs.length>0) {
+ List<String> suffixes = new ArrayList<String>();
+ for (Language lang : langs) {
+ if (lang.getFileSuffixes() != null) {
+ suffixes.addAll(Arrays.asList(lang.getFileSuffixes()));
+ }
+ }
+ if (!suffixes.isEmpty()) {
+ suffixFilter = new SuffixFileFilter(suffixes);
+ }
+ }
+
+ return suffixFilter;
+ }
+
+ private static class ExclusionFilter implements IOFileFilter {
+ File sourceDir;
+ WildcardPattern[] patterns;
+
+ ExclusionFilter(File sourceDir, WildcardPattern[] patterns) {
+ this.sourceDir = sourceDir;
+ this.patterns = patterns;
+ }
+
+ public boolean accept(File file) {
+ String relativePath = getRelativePath(file, sourceDir);
+ if (relativePath == null) {
+ return false;
+ }
+ for (WildcardPattern pattern : patterns) {
+ if (pattern.match(relativePath)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean accept(File file, String name) {
+ return accept(file);
+ }
+ }
+
+ /**
+ * Save data into a new file of Sonar working directory.
+ *
+ * @return the created file
+ */
+ public File writeToWorkingDirectory(String content, String fileName) throws IOException {
+ return writeToFile(content, getSonarWorkingDirectory(), fileName);
+ }
+
+ protected static File writeToFile(String content, File dir, String fileName) throws IOException {
+ File file = new File(dir, fileName);
+ FileUtils.writeStringToFile(file, content, CharEncoding.UTF_8);
+ return file;
+ }
+
+ /**
+ * getRelativePath("c:/foo/src/my/package/Hello.java", "c:/foo/src") is "my/package/Hello.java"
+ *
+ * @return null if file is not in dir (including recursive subdirectories)
+ */
+ public static String getRelativePath(File file, File dir) {
+ return getRelativePath(file, Arrays.asList(dir));
+ }
+
+ /**
+ * getRelativePath("c:/foo/src/my/package/Hello.java", ["c:/bar", "c:/foo/src"]) is "my/package/Hello.java".
+ * <p/>
+ * <p>Relative path is composed of slashes. Windows backslaches are replaced by /</p>
+ *
+ * @return null if file is not in dir (including recursive subdirectories)
+ */
+ public static String getRelativePath(File file, List<File> dirs) {
+ List<String> stack = new ArrayList<String>();
+ String path = FilenameUtils.normalize(file.getAbsolutePath());
+ File cursor = new File(path);
+ while (cursor != null) {
+ if (containsFile(dirs, cursor)) {
+ return StringUtils.join(stack, "/");
+ }
+ stack.add(0, cursor.getName());
+ cursor = cursor.getParentFile();
+ }
+ return null;
+ }
+
+ public File getFileFromBuildDirectory(String filename) {
+ File file = new File(getBuildDir(), filename);
+ return (file.exists() ? file : null);
+ }
+
+ public Resource toResource(File file) {
+ if (file == null || !file.exists()) {
+ return null;
+ }
+
+ String relativePath = getRelativePath(file, getSourceDirs());
+ if (relativePath == null) {
+ return null;
+ }
+
+ return (file.isFile() ? new org.sonar.api.resources.File(relativePath) : new org.sonar.api.resources.Directory(relativePath));
+ }
+
+ private static boolean containsFile(List<File> dirs, File cursor) {
+ for (File dir : dirs) {
+ if (FilenameUtils.equalsNormalizedOnSystem(dir.getAbsolutePath(), cursor.getAbsolutePath())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Directory.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Directory.java
new file mode 100644
index 00000000000..ebf2b745c42
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Directory.java
@@ -0,0 +1,97 @@
+/*
+ * 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.resources;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.utils.WildcardPattern;
+
+/**
+ * @since 1.10
+ */
+public class Directory extends Resource {
+
+ public static final String SEPARATOR = "/";
+ public static final String ROOT = "[root]";
+
+ private Language language;
+
+ public Directory(String key) {
+ this(key, null);
+ }
+
+ public Directory(String key, Language language) {
+ setKey(parseKey(key));
+ this.language = language;
+ }
+
+ public String getName() {
+ return getKey();
+ }
+
+ public String getLongName() {
+ return null;
+ }
+
+ public String getDescription() {
+ return null;
+ }
+
+ public Language getLanguage() {
+ return language;
+ }
+
+ public String getScope() {
+ return Resource.SCOPE_SPACE;
+ }
+
+ public String getQualifier() {
+ return Resource.QUALIFIER_DIRECTORY;
+ }
+
+ public Resource getParent() {
+ return null;
+ }
+
+ public boolean matchFilePattern(String antPattern) {
+ WildcardPattern matcher = WildcardPattern.create(antPattern, "/");
+ return matcher.match(getKey());
+ }
+
+ public static String parseKey(String key) {
+ if (StringUtils.isBlank(key)) {
+ return ROOT;
+ }
+
+ key = key.replace('\\', '/');
+ key = StringUtils.trim(key);
+ key = StringUtils.removeStart(key, Directory.SEPARATOR);
+ key = StringUtils.removeEnd(key, Directory.SEPARATOR);
+ return key;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("key", getKey())
+ .append("language", language)
+ .toString();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/File.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/File.java
new file mode 100644
index 00000000000..5d7fb94d212
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/File.java
@@ -0,0 +1,207 @@
+/*
+ * 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.resources;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.utils.WildcardPattern;
+
+import java.util.List;
+
+/**
+ * This class is an implementation of a resource of type FILE
+ * @since 1.10
+ */
+public class File extends Resource<Directory> {
+
+ private String directoryKey;
+ private String filename;
+ private Language language;
+ private Directory parent;
+
+ /**
+ * File in project. Key is the path relative to project source directories. It is not the absolute path
+ * and it does not include the path to source directories. Example : <code>new File("org/sonar/foo.sql")</code>. The
+ * absolute path may be c:/myproject/src/main/sql/org/sonar/foo.sql. Project root is c:/myproject and source dir
+ * is src/main/sql.
+ */
+ public File(String key) {
+ if (key == null) {
+ throw new IllegalArgumentException("File key is null");
+ }
+ String realKey = parseKey(key);
+ if (realKey.indexOf(Directory.SEPARATOR) >= 0) {
+ this.directoryKey = Directory.parseKey(StringUtils.substringBeforeLast(key, Directory.SEPARATOR));
+ this.filename = StringUtils.substringAfterLast(realKey, Directory.SEPARATOR);
+ realKey = new StringBuilder().append(this.directoryKey).append(Directory.SEPARATOR).append(filename).toString();
+
+ } else {
+ this.filename = key;
+ }
+ setKey(realKey);
+ }
+
+ /**
+ * Creates a file from its containing directory and name
+ */
+ public File(String directory, String filename) {
+ this.filename = StringUtils.trim(filename);
+ if (StringUtils.isBlank(directory)) {
+ setKey(filename);
+
+ } else {
+ this.directoryKey = Directory.parseKey(directory);
+ setKey(new StringBuilder().append(directoryKey).append(Directory.SEPARATOR).append(this.filename).toString());
+ }
+ }
+
+ /**
+ * Creates a File from its language and its key
+ */
+ public File(Language language, String key) {
+ this(key);
+ this.language = language;
+ }
+
+ /**
+ * Creates a File from language, directory and filename
+ */
+ public File(Language language, String directory, String filename) {
+ this(directory, filename);
+ this.language = language;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see Resource#getParent()
+ */
+ public Directory getParent() {
+ if (parent == null) {
+ parent = new Directory(directoryKey);
+ }
+ return parent;
+ }
+
+ private static String parseKey(String key) {
+ if (StringUtils.isBlank(key)) {
+ return null;
+ }
+
+ key = key.replace('\\', '/');
+ key = StringUtils.trim(key);
+ return key;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see Resource#matchFilePattern(String)
+ */
+ public boolean matchFilePattern(String antPattern) {
+ WildcardPattern matcher = WildcardPattern.create(antPattern, "/");
+ return matcher.match(getKey());
+ }
+
+ /**
+ * Creates a File from an io.file and a list of sources directories
+ */
+ public static File fromIOFile(java.io.File file, List<java.io.File> sourceDirs) {
+ String relativePath = DefaultProjectFileSystem.getRelativePath(file, sourceDirs);
+ if (relativePath != null) {
+ return new File(relativePath);
+ }
+ return null;
+ }
+
+ /**
+ * Creates a File from its name and a project
+ */
+ public static File fromIOFile(java.io.File file, Project project) {
+ return fromIOFile(file, project.getFileSystem().getSourceDirs());
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see Resource#getName()
+ */
+ public String getName() {
+ return filename;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see Resource#getLongName()
+ */
+ public String getLongName() {
+ return getKey();
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see Resource#getDescription()
+ */
+ public String getDescription() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see Resource#getLanguage()
+ */
+ public Language getLanguage() {
+ return language;
+ }
+
+ /**
+ * Sets the language of the file
+ */
+ public void setLanguage(Language language) {
+ this.language = language;
+ }
+
+ /**
+ * @return SCOPE_ENTITY
+ */
+ public String getScope() {
+ return Resource.SCOPE_ENTITY;
+ }
+
+ /**
+ * @return QUALIFIER_FILE
+ */
+ public String getQualifier() {
+ return Resource.QUALIFIER_FILE;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("key", getKey())
+ .append("dir", directoryKey)
+ .append("filename", filename)
+ .append("language", language)
+ .toString();
+ }
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Java.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Java.java
new file mode 100644
index 00000000000..4f5f22c683e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Java.java
@@ -0,0 +1,60 @@
+/*
+ * 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.resources;
+
+/**
+ * Java language implementation
+ *
+ * @since 1.10
+ */
+public class Java extends AbstractLanguage {
+
+ public static final Java INSTANCE = new Java();
+
+ /**
+ * Java key
+ */
+ public static final String KEY = "java";
+ /**
+ * Default package name for classes without package def
+ */
+ public static final String DEFAULT_PACKAGE_NAME = "[default]";
+ /**
+ * Java files knows suffixes
+ */
+ public static final String[] SUFFIXES = {"java", "jav"};
+
+ /**
+ * Default constructor
+ */
+ public Java() {
+ super(KEY, "Java");
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see AbstractLanguage#getFileSuffixes()
+ */
+ public String[] getFileSuffixes() {
+ return SUFFIXES;
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/JavaFile.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/JavaFile.java
new file mode 100644
index 00000000000..c2ece8bbb35
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/JavaFile.java
@@ -0,0 +1,233 @@
+/*
+ * 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.resources;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.utils.WildcardPattern;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * A class that represents a Java class. This class can either be a Test class or source class
+ *
+ * @since 1.10
+ */
+public class JavaFile extends Resource<JavaPackage> {
+
+ private String filename;
+ private String longName;
+ private String packageKey;
+ private boolean unitTest;
+ private JavaPackage parent = null;
+
+ /**
+ * Creates a JavaFile that is not a class of test based on package and file names
+ */
+ public JavaFile(String packageName, String className) {
+ this(packageName, className, false);
+ }
+
+ /**
+ * Creates a JavaFile that can be of any type based on package and file names
+ *
+ * @param unitTest whether it is a unit test file or a source file
+ */
+ public JavaFile(String packageKey, String className, boolean unitTest) {
+ if (className==null) {
+ throw new IllegalArgumentException("Java filename can not be null");
+ }
+ if (className.indexOf('$') >= 0) {
+ throw new IllegalArgumentException("Java inner classes are not supported : " + className);
+ }
+ this.filename = StringUtils.trim(className);
+ String key;
+ if (StringUtils.isBlank(packageKey)) {
+ this.packageKey = JavaPackage.DEFAULT_PACKAGE_NAME;
+ this.longName = this.filename;
+ key = new StringBuilder().append(this.packageKey).append(".").append(this.filename).toString();
+ } else {
+ this.packageKey = packageKey.trim();
+ key = new StringBuilder().append(this.packageKey).append(".").append(this.filename).toString();
+ this.longName = key;
+ }
+ setKey(key);
+ this.unitTest = unitTest;
+ }
+
+ /**
+ * Creates a source file from its key
+ */
+ public JavaFile(String key) {
+ this(key, false);
+ }
+
+ /**
+ * Creates any JavaFile from its key
+ *
+ * @param unitTest whether it is a unit test file or a source file
+ */
+ public JavaFile(String key, boolean unitTest) {
+ if (key==null) {
+ throw new IllegalArgumentException("Java filename can not be null");
+ }
+ if (key != null && key.indexOf('$') >= 0) {
+ throw new IllegalArgumentException("Java inner classes are not supported : " + key);
+ }
+ String realKey = StringUtils.trim(key);
+ this.unitTest = unitTest;
+
+ if (realKey.contains(".")) {
+ this.filename = StringUtils.substringAfterLast(realKey, ".");
+ this.packageKey = StringUtils.substringBeforeLast(realKey, ".");
+ this.longName = realKey;
+
+ } else {
+ this.filename = realKey;
+ this.longName = realKey;
+ this.packageKey = JavaPackage.DEFAULT_PACKAGE_NAME;
+ realKey = new StringBuilder().append(JavaPackage.DEFAULT_PACKAGE_NAME).append(".").append(realKey).toString();
+ }
+ setKey(realKey);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public JavaPackage getParent() {
+ if (parent == null) {
+ parent = new JavaPackage(packageKey);
+ }
+ return parent;
+ }
+
+ /**
+ * @return null
+ */
+ public String getDescription() {
+ return null;
+ }
+
+ /**
+ * @return Java
+ */
+ public Language getLanguage() {
+ return Java.INSTANCE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return filename;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getLongName() {
+ return longName;
+ }
+
+ /**
+ * @return SCOPE_ENTITY
+ */
+ public String getScope() {
+ return Resource.SCOPE_ENTITY;
+ }
+
+ /**
+ * @return QUALIFIER_UNIT_TEST_CLASS or QUALIFIER_CLASS depending whether it is a unit test class
+ */
+ public String getQualifier() {
+ return unitTest ? Resource.QUALIFIER_UNIT_TEST_CLASS : Resource.QUALIFIER_CLASS;
+ }
+
+ /**
+ * @return whether the JavaFile is a unit test class or not
+ */
+ public boolean isUnitTest() {
+ return unitTest;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean matchFilePattern(String antPattern) {
+ if (unitTest) {
+ return false;
+ }
+ String fileKey = getKey();
+ if (!fileKey.endsWith(".java")) {
+ fileKey += ".java";
+ }
+ if (StringUtils.substringAfterLast(antPattern, "/").indexOf(".")<0) {
+ antPattern += ".*";
+ }
+ WildcardPattern matcher = WildcardPattern.create(antPattern, ".");
+ return matcher.match(fileKey);
+ }
+
+ /**
+ * Creates a JavaFile from a file in the source directories
+ *
+ * @return the JavaFile created if exists, null otherwise
+ */
+ public static JavaFile fromIOFile(File file, List<File> sourceDirs, boolean unitTest) {
+ if (file == null || !StringUtils.endsWithIgnoreCase(file.getName(), ".java")) {
+ return null;
+ }
+ String relativePath = DefaultProjectFileSystem.getRelativePath(file, sourceDirs);
+ if (relativePath != null) {
+ String pacname = null;
+ String classname = relativePath;
+
+ if (relativePath.indexOf('/') >= 0) {
+ pacname = StringUtils.substringBeforeLast(relativePath, "/");
+ pacname = StringUtils.replace(pacname, "/", ".");
+ classname = StringUtils.substringAfterLast(relativePath, "/");
+ }
+ classname = StringUtils.substringBeforeLast(classname, ".");
+ return new JavaFile(pacname, classname, unitTest);
+ }
+ return null;
+ }
+
+ /**
+ * Shortcut to fromIOFile with an abolute path
+ */
+ public static JavaFile fromAbsolutePath(String path, List<File> sourceDirs, boolean unitTest) {
+ if (path == null) {
+ return null;
+ }
+ return fromIOFile(new File(path), sourceDirs, unitTest);
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("key", getKey())
+ .append("package", packageKey)
+ .append("longName", longName)
+ .append("unitTest", unitTest)
+ .toString();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/JavaPackage.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/JavaPackage.java
new file mode 100644
index 00000000000..ef3aa1fc236
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/JavaPackage.java
@@ -0,0 +1,122 @@
+/*
+ * 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.resources;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.utils.WildcardPattern;
+
+/**
+ * A class that represents a Java package in Sonar
+ *
+ * @since 1.10
+ */
+public class JavaPackage extends Resource {
+
+ /**
+ * Default package name for classes without package definition
+ */
+ public static final String DEFAULT_PACKAGE_NAME = "[default]";
+
+ /**
+ * Defaul constructor
+ */
+ public JavaPackage() {
+ this(null);
+ }
+
+ /**
+ * Creates a JavaPackage from its key. Will use DEFAULT_PACKAGE_NAME if key is null
+ */
+ public JavaPackage(String key) {
+ setKey(StringUtils.defaultIfEmpty(StringUtils.trim(key), DEFAULT_PACKAGE_NAME));
+ }
+
+ /**
+ * @return whether the JavaPackage key is the defult key
+ */
+ public boolean isDefault() {
+ return StringUtils.equals(getKey(), DEFAULT_PACKAGE_NAME);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean matchFilePattern(String antPattern) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getDescription() {
+ return null;
+ }
+
+ /**
+ * @return SCOPE_SPACE
+ */
+ public String getScope() {
+ return Resource.SCOPE_SPACE;
+ }
+
+ /**
+ * @return QUALIFIER_PACKAGE
+ */
+ public String getQualifier() {
+ return Resource.QUALIFIER_PACKAGE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getName() {
+ return getKey();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Resource<?> getParent() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getLongName() {
+ return null;
+ }
+
+ /**
+ * @return Java
+ */
+ public Language getLanguage() {
+ return Java.INSTANCE;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("id", getId())
+ .append("key", getKey())
+ .toString();
+ }
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Language.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Language.java
new file mode 100644
index 00000000000..a86ffb650e5
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Language.java
@@ -0,0 +1,47 @@
+/*
+ * 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.resources;
+
+import org.sonar.api.BatchExtension;
+import org.sonar.api.ServerExtension;
+
+/**
+ * The extension point to define a new language
+ *
+ * @since 1.10
+ */
+public interface Language extends BatchExtension, ServerExtension {
+
+ /**
+ * For example "java". Should not be more than 5 chars.
+ */
+ String getKey();
+
+ /**
+ * For example "Java"
+ */
+ String getName();
+
+ /**
+ * For example ["jav","java"]. If empty, then all files in source directories are considered as sources.
+ */
+ String[] getFileSuffixes();
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Languages.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Languages.java
new file mode 100644
index 00000000000..372100fe4ae
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Languages.java
@@ -0,0 +1,77 @@
+/*
+ * 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.resources;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.ServerComponent;
+
+import java.util.*;
+
+/**
+ * A class to store the list of languages
+ *
+ * @since 1.10
+ */
+public class Languages implements BatchComponent, ServerComponent {
+
+ private final Map<String, Language> map = new HashMap<String, Language>();
+
+ /**
+ * Creates a list of languages
+ */
+ public Languages(Language... languages) {
+ if (languages != null) {
+ for (Language language : languages) {
+ map.put(language.getKey(), language);
+ }
+ }
+ }
+
+ /**
+ * @param keys the languages keys
+ * @return * @return the list of suffix files associates to languages included in the current object
+ */
+
+ public String[] getSuffixes(String... keys) {
+ List<String> suffixes = new ArrayList<String>();
+
+ for (Map.Entry<String, Language> entry : map.entrySet()) {
+ if (ArrayUtils.isEmpty(keys) || ArrayUtils.contains(keys, entry.getKey())) {
+ suffixes.addAll(Arrays.asList(entry.getValue().getFileSuffixes()));
+ }
+ }
+ return suffixes.toArray(new String[suffixes.size()]);
+ }
+
+ /**
+ * Return a language from the current object based on its key
+ */
+ public Language get(String key) {
+ return map.get(key);
+ }
+
+ /**
+ * Adds a language to the current object
+ */
+ public void add(Language language) {
+ map.put(language.getKey(), language);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Library.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Library.java
new file mode 100644
index 00000000000..84daac57437
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Library.java
@@ -0,0 +1,121 @@
+/*
+ * 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.resources;
+
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+public class Library extends Resource {
+
+ private String name;
+ private String description;
+ private String version;
+
+ public Library(String key, String version) {
+ setKey(key);
+ this.version = version;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public Library setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Library setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getLongName() {
+ return null;
+ }
+
+ @Override
+ public String getDescription() {
+ return description;
+ }
+
+ @Override
+ public Language getLanguage() {
+ return null;
+ }
+
+ @Override
+ public String getScope() {
+ return Resource.SCOPE_PROJECT;
+ }
+
+ @Override
+ public String getQualifier() {
+ return Resource.QUALIFIER_LIB;
+ }
+
+ @Override
+ public Resource getParent() {
+ return null;
+ }
+
+ @Override
+ public boolean matchFilePattern(String antPattern) {
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Library library = (Library) o;
+ if (!getKey().equals(library.getKey())) {
+ return false;
+ }
+ return version.equals(library.version);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + getKey().hashCode();
+ result = 31 * result + version.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("key", getKey())
+ .append("name", getName())
+ .append("version", version)
+ .toString();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Project.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Project.java
new file mode 100644
index 00000000000..7e4371a0e1f
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Project.java
@@ -0,0 +1,412 @@
+/*
+ * 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.resources;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.maven.project.MavenProject;
+import org.sonar.api.CoreProperties;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * A class that manipulates Projects in the Sonar way, i.e. mixing MavenProjects with the way it should be analyzed
+ *
+ * @since 1.10
+ */
+public class Project extends Resource {
+
+ /**
+ * @deprecated since version 1.11. Constant moved to CoreProperties
+ */
+ @Deprecated
+ public static final String PARAM_VERSION = "sonar.projectVersion";
+
+ /**
+ * @deprecated since version 1.11. Constant moved to CoreProperties
+ */
+ @Deprecated
+ public static final String PARAM_DATE = "sonar.projectDate";
+
+ /**
+ * @deprecated since version 1.11. Constant moved to CoreProperties
+ */
+ @Deprecated
+ public static final String PARAM_LANGUAGE = "sonar.language";
+
+ /**
+ * @deprecated since version 1.11. Constant moved to CoreProperties
+ */
+ @Deprecated
+ public static final String PARAM_DYNAMIC_ANALYSIS = "sonar.dynamicAnalysis";
+
+ /**
+ * @deprecated since version 1.11. Constant moved to CoreProperties
+ */
+ @Deprecated
+ public static final String PARAM_EXCLUSIONS = "sonar.exclusions";
+
+ /**
+ * @deprecated since version 1.11. Constant moved to CoreProperties
+ */
+ @Deprecated
+ public static final String PARAM_REUSE_RULES_CONFIG = "sonar.reuseExistingRulesConfiguration";
+
+
+ /**
+ * Enumerates the type of possible analysis
+ */
+ public enum AnalysisType {
+ STATIC, DYNAMIC, REUSE_REPORTS;
+
+ /**
+ * @param includeReuseReportMode whether to count report reuse as dynamic or not
+ * @return whether this a dynamic analysis
+ */
+ public boolean isDynamic(boolean includeReuseReportMode) {
+ return equals(Project.AnalysisType.DYNAMIC) ||
+ (equals(Project.AnalysisType.REUSE_REPORTS) && includeReuseReportMode);
+ }
+ }
+
+ private MavenProject pom;
+ private String branch;
+ private ProjectFileSystem fileSystem;
+ private Configuration configuration;
+ private String name;
+ private String description;
+ private String packaging;
+ private Language language;
+ private String languageKey;
+ private Date analysisDate;
+ private AnalysisType analysisType;
+ private String[] exclusionPatterns;
+ private String analysisVersion;
+ private boolean latestAnalysis;
+
+ // modules tree
+ private Project parent;
+ private List<Project> modules = new ArrayList<Project>();
+
+ public Project(String key) {
+ setKey(key);
+ }
+
+ public Project(String key, String branch, String name) {
+ if (StringUtils.isNotBlank(branch)) {
+ setKey(String.format("%s:%s", key, branch));
+ this.name = String.format("%s %s", name, branch);
+ } else {
+ setKey(key);
+ this.name = name;
+ }
+ this.branch = branch;
+ }
+
+ public String getBranch() {
+ return branch;
+ }
+
+ public Project setBranch(String branch) {
+ this.branch = branch;
+ return this;
+ }
+
+ public final Project setPom(MavenProject pom) {
+ this.pom = pom;
+ return this;
+ }
+
+ /**
+ * @return the project's packaging
+ */
+ public String getPackaging() {
+ return packaging;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getLongName() {
+ return null;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public Project setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public Project setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ public Project setPackaging(String packaging) {
+ this.packaging = packaging;
+ return this;
+ }
+
+ /**
+ * @return whether the current project is root project
+ */
+ public boolean isRoot() {
+ return getParent() == null;
+ }
+
+ public Project getRoot() {
+ return (parent == null ? this : parent.getRoot());
+ }
+
+ /**
+ * @return whether the current project is a module
+ */
+ public boolean isModule() {
+ return !isRoot();
+ }
+
+ /**
+ * @return the type of analysis of the project
+ */
+ public AnalysisType getAnalysisType() {
+ return analysisType;
+ }
+
+ public Project setAnalysisType(AnalysisType at) {
+ this.analysisType = at;
+ return this;
+ }
+
+ /**
+ * whether it's the latest analysis done on this project (displayed in sonar dashboard) or an analysis on a past revision.
+ * @since 2.0
+ */
+ public boolean isLatestAnalysis() {
+ return latestAnalysis;
+ }
+
+ /**
+ * For internal use only.
+ */
+ public Project setLatestAnalysis(boolean b) {
+ this.latestAnalysis = b;
+ return this;
+ }
+
+ /**
+ * Used for Maven projects
+ */
+ public String getGroupId() {
+ return pom.getGroupId();
+ }
+
+ /**
+ * Used for Maven projects
+ */
+ public String getArtifactId() {
+ return pom.getArtifactId();
+ }
+
+ /**
+ * @return the project language
+ */
+ public Language getLanguage() {
+ return language;
+ }
+
+ public Project setLanguage(Language language) {
+ this.language = language;
+ return this;
+ }
+
+ /**
+ * @return the language key
+ */
+ public String getLanguageKey() {
+ return languageKey;
+ }
+
+ /**
+ * For internal use only.
+ */
+ public Project setLanguageKey(String languageKey) {
+ this.languageKey = languageKey;
+ return this;
+ }
+
+ /**
+ * For internal use only.
+ */
+ public Project setAnalysisDate(Date analysisDate) {
+ this.analysisDate = analysisDate;
+ return this;
+ }
+
+ /**
+ * For internal use only.
+ */
+ public Project setAnalysisVersion(String analysisVersion) {
+ this.analysisVersion = analysisVersion;
+ return this;
+ }
+
+ /**
+ * @return the scope of the current object
+ */
+ public String getScope() {
+ return SCOPE_SET;
+ }
+
+ /**
+ * @return the qualifier of the current object
+ */
+ public String getQualifier() {
+ return isRoot() ? QUALIFIER_PROJECT : QUALIFIER_MODULE;
+ }
+
+ @Override
+ public boolean matchFilePattern(String antPattern) {
+ return false;
+ }
+
+ public Project getParent() {
+ return parent;
+ }
+
+ /**
+ * For internal use only.
+ */
+ public Project setParent(Project parent) {
+ this.parent = parent;
+ if (parent != null) {
+ parent.modules.add(this);
+ }
+ return this;
+ }
+
+ /**
+ * For internal use only.
+ */
+ public void removeFromParent() {
+ if (parent!=null) {
+ parent.modules.remove(this);
+ }
+ }
+
+ /**
+ * @return the list of modules
+ */
+ public List<Project> getModules() {
+ return modules;
+ }
+
+ /**
+ * @return whether to use external source for rules configuration
+ */
+ public boolean getReuseExistingRulesConfig() {
+ return (configuration != null && configuration.getBoolean(CoreProperties.REUSE_RULES_CONFIGURATION_PROPERTY, false));
+ }
+
+ /**
+ * @return the current version of the project
+ */
+ public String getAnalysisVersion() {
+ return analysisVersion;
+ }
+
+ /**
+ * @return the analysis date, i.e. the date that will be used to store the snapshot
+ */
+ public Date getAnalysisDate() {
+ return analysisDate;
+ }
+
+ /**
+ * Patterns of resource exclusion as defined in project settings page.
+ */
+ public String[] getExclusionPatterns() {
+ return exclusionPatterns;
+ }
+
+ /**
+ * Set exclusion patterns. Configuration is not saved, so this method must be used ONLY IN UNIT TESTS.
+ */
+ public Project setExclusionPatterns(String[] s) {
+ this.exclusionPatterns = s;
+ return this;
+ }
+
+ public ProjectFileSystem getFileSystem() {
+ return fileSystem;
+ }
+
+ public Project setFileSystem(ProjectFileSystem fs) {
+ this.fileSystem = fs;
+ return this;
+ }
+
+ /**
+ * @return the underlying maven project
+ */
+ public MavenProject getPom() {
+ return pom;
+ }
+
+ /**
+ * @return the project configuration
+ */
+ public Configuration getConfiguration() {
+ return configuration;
+ }
+
+ /**
+ * Sets the configuration
+ *
+ * @return the current object
+ */
+ public final Project setConfiguration(Configuration configuration) {
+ this.configuration = configuration;
+ return this;
+ }
+
+ public Object getProperty(String key) {
+ return configuration != null ? configuration.getProperty(key) : null;
+ }
+
+ public static Project createFromMavenIds(String groupId, String artifactId) {
+ return new Project(String.format("%s:%s", groupId, artifactId));
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("id", getId())
+ .append("key", getKey())
+ .append("qualifier", getQualifier())
+ .toString();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/ProjectFileSystem.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/ProjectFileSystem.java
new file mode 100644
index 00000000000..24a47bfe1a0
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/ProjectFileSystem.java
@@ -0,0 +1,104 @@
+/*
+ * 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.resources;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.List;
+
+/**
+ * @since 1.10
+ */
+public interface ProjectFileSystem {
+ /**
+ * Source encoding. It's the default plateform charset if it is not defined in the project
+ * (Maven property 'project.build.sourceEncoding').
+ */
+ Charset getSourceCharset();
+
+ /**
+ * Project root directory.
+ */
+ File getBasedir();
+
+ /**
+ * Build directory. It's "${basedir}/target" by default in Maven projects.
+ */
+ File getBuildDir();
+
+ File getBuildOutputDir();
+
+ List<File> getSourceDirs();
+
+ ProjectFileSystem addSourceDir(File dir);
+
+ List<File> getTestDirs();
+
+ ProjectFileSystem addTestDir(File dir);
+
+ File getReportOutputDir();
+
+ File getSonarWorkingDirectory();
+
+ /**
+ * Get file from path. It can be absolute or relative to project basedir. For example resolvePath("pom.xml") or resolvePath("src/main/java")
+ */
+ File resolvePath(String path);
+
+ /**
+ * Source files, excluding unit tests and files matching project exclusion patterns.
+ *
+ * @param langs language filter. Check all files, whatever their language, if null or empty.
+ */
+ List<File> getSourceFiles(Language... langs);
+
+ /**
+ * Java source files, excluding unit tests and files matching project exclusion patterns.
+ * Shortcut for getSourceFiles(Java.INSTANCE)
+ */
+ List<File> getJavaSourceFiles();
+
+ /**
+ * Check if the project has Java files, excluding unit tests and files matching project exclusion patterns.
+ */
+ boolean hasJavaSourceFiles();
+
+ /**
+ * Unit test files, excluding files matching project exclusion patterns.
+ */
+ List<File> getTestFiles(Language... langs);
+
+ /**
+ * Check if the project has unit test files, excluding files matching project exclusion patterns.
+ */
+ boolean hasTestFiles(Language lang);
+
+ /**
+ * Save data into a new file of Sonar working directory.
+ *
+ * @return the created file
+ */
+ File writeToWorkingDirectory(String content, String fileName) throws IOException;
+
+ File getFileFromBuildDirectory(String filename);
+
+ Resource toResource(File file);
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/ProjectLink.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/ProjectLink.java
new file mode 100644
index 00000000000..f8353c02f0d
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/ProjectLink.java
@@ -0,0 +1,134 @@
+/*
+ * 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.resources;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.database.BaseIdentifiable;
+import org.sonar.api.database.model.ResourceModel;
+
+import javax.persistence.*;
+
+/**
+ * @since 1.10
+ */
+@Entity(name = "ProjectLink")
+@Table(name = "project_links")
+public class ProjectLink extends BaseIdentifiable {
+
+ public static final int NAME_COLUMN_SIZE = 128;
+ public static final int HREF_COLUMN_SIZE = 2048;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "project_id", updatable = false, nullable = false)
+ private ResourceModel resource;
+
+ @Column(name = "link_type", updatable = true, nullable = true, length = 20)
+ private String key;
+
+ @Column(name = "name", updatable = true, nullable = true, length = NAME_COLUMN_SIZE)
+ private String name;
+
+ @Column(name = "href", updatable = true, nullable = false, length = HREF_COLUMN_SIZE)
+ private String href;
+
+ public ProjectLink() {
+ }
+
+ public ProjectLink(String key, String name, String href) {
+ this.key = key;
+ setName(name);
+ setHref(href);
+ }
+
+ public ResourceModel getResource() {
+ return resource;
+ }
+
+ public void setResource(ResourceModel resource) {
+ this.resource = resource;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public final void setName(String name) {
+ this.name = StringUtils.abbreviate(name, NAME_COLUMN_SIZE);
+ }
+
+ public String getHref() {
+ return href;
+ }
+
+ public final void setHref(String href) {
+ if (href == null) {
+ throw new IllegalArgumentException("ProjectLink.href can not be null");
+ }
+ this.href = StringUtils.abbreviate(href, HREF_COLUMN_SIZE);
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ProjectLink that = (ProjectLink) o;
+ if (!key.equals(that.key)) {
+ return false;
+ }
+ return resource.equals(that.resource);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = resource != null ? resource.hashCode() : 0;
+ result = 31 * result + key.hashCode();
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("key", key)
+ .append("name", name)
+ .append("href", href)
+ .append("resource", resource)
+ .toString();
+ }
+
+ public void copyFieldsFrom(ProjectLink link) {
+ this.name = link.getName();
+ this.href = link.getHref();
+ }
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/ProjectUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/ProjectUtils.java
new file mode 100644
index 00000000000..1ad585acbe3
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/ProjectUtils.java
@@ -0,0 +1,43 @@
+/*
+ * 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.resources;
+
+import org.sonar.api.batch.maven.MavenUtils;
+
+/**
+ * @since 1.10
+ */
+public final class ProjectUtils {
+
+ private ProjectUtils() {
+ // utility class with only static methods
+ }
+
+ /**
+ * Java version as defined in maven-compiler-plugin
+ */
+ public static String getJavaVersion(Project project) {
+ return MavenUtils.getJavaVersion(project.getPom());
+ }
+
+ public static String getJavaSourceVersion(Project project) {
+ return MavenUtils.getJavaSourceVersion(project.getPom());
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Resource.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Resource.java
new file mode 100644
index 00000000000..32862e6bd7d
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Resource.java
@@ -0,0 +1,197 @@
+/*
+ * 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.resources;
+
+/**
+ * The interface to implement to create a resource in Sonar
+ *
+ * @since 1.10
+ */
+public abstract class Resource<PARENT extends Resource> {
+
+ public static final String SCOPE_SET = "PRJ";
+ public static final String SCOPE_SPACE = "DIR";
+ public static final String SCOPE_ENTITY = "FIL";
+
+ /**
+ * Use SCOPE_SET instead
+ */
+ @Deprecated
+ public static final String SCOPE_PROJECT = SCOPE_SET;
+
+ /**
+ * Use SCOPE_SPACE instead
+ */
+ @Deprecated
+ public static final String SCOPE_DIRECTORY = SCOPE_SPACE;
+
+ /**
+ * Use SCOPE_ENTITY instead
+ */
+ @Deprecated
+ public static final String SCOPE_FILE = SCOPE_ENTITY;
+
+
+ public static final String QUALIFIER_VIEW = "VW";
+ public static final String QUALIFIER_SUBVIEW = "SVW";
+ public static final String QUALIFIER_LIB = "LIB";
+ public static final String QUALIFIER_PROJECT = "TRK";
+ public static final String QUALIFIER_MODULE = "BRC";
+ public static final String QUALIFIER_PACKAGE = "PAC";
+ public static final String QUALIFIER_DIRECTORY = "DIR";
+ public static final String QUALIFIER_FILE = "FIL";
+ public static final String QUALIFIER_CLASS = "CLA";
+ public static final String QUALIFIER_FIELD = "FLD";
+ public static final String QUALIFIER_METHOD = "MET";
+ public static final String QUALIFIER_UNIT_TEST_CLASS = "UTS";
+
+ /**
+ * Use QUALIFIER_PROJECT instead
+ */
+ @Deprecated
+ public static final String QUALIFIER_PROJECT_TRUNK = QUALIFIER_PROJECT;
+
+ /**
+ * Use QUALIFIER_MODULE instead
+ */
+ @Deprecated
+ public static final String QUALIFIER_PROJECT_BRANCH = QUALIFIER_MODULE;
+
+ private Integer id = null;
+
+ private String key = null;
+
+ private String effectiveKey = null;
+
+ private boolean isExcluded = false;
+
+
+ /**
+ * @return the resource key
+ */
+ public final String getKey() {
+ return key;
+ }
+
+ protected void setKey(String s) {
+ this.key = s;
+ }
+
+ /**
+ * @return the resource name
+ */
+ public abstract String getName();
+
+ /**
+ * @return the resource long name
+ */
+ public abstract String getLongName();
+
+ /**
+ * @return the resource description
+ */
+ public abstract String getDescription();
+
+ /**
+ * @return the language
+ */
+ public abstract Language getLanguage();
+
+ /**
+ * @return the scope
+ */
+ public abstract String getScope();
+
+ /**
+ * @return the qualifier
+ */
+ public abstract String getQualifier();
+
+ /**
+ * The parent is used to build the resources tree, for example for relations between classes, packages and projects.
+ * <p>Return null if the parent is the project.</p>
+ */
+ public abstract PARENT getParent();
+
+ /**
+ * Check resource against an Ant pattern, like mypackag?/*Foo.java. It's used for example
+ * to match resource exclusions.
+ *
+ * @param antPattern Ant-like pattern (with **, * and ?). It includes file suffixes.
+ * @return true if the resource matches the Ant pattern
+ */
+ public abstract boolean matchFilePattern(String antPattern);
+
+
+ public final Integer getId() {
+ return id;
+ }
+
+ /**
+ * Internal use only
+ */
+ public Resource setId(Integer id) {
+ this.id = id;
+ return this;
+ }
+
+ public final String getEffectiveKey() {
+ return effectiveKey;
+ }
+
+ /**
+ * Internal use only
+ */
+ public final Resource setEffectiveKey(String effectiveKey) {
+ this.effectiveKey = effectiveKey;
+ return this;
+ }
+
+ public final boolean isExcluded() {
+ return isExcluded;
+ }
+
+ /**
+ * Internal use only
+ */
+ public final Resource setExcluded(boolean b) {
+ isExcluded = b;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Resource resource = (Resource) o;
+ return key.equals(resource.key);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return key.hashCode();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceUtils.java
new file mode 100644
index 00000000000..830478bede9
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/resources/ResourceUtils.java
@@ -0,0 +1,130 @@
+/*
+ * 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.resources;
+
+/**
+ * @since 1.10
+ */
+public final class ResourceUtils {
+
+ private ResourceUtils() {
+ }
+
+ /**
+ * @return whether the resource is a view
+ */
+ public static boolean isView(Resource resource) {
+ return isSet(resource) && Resource.QUALIFIER_VIEW.equals(resource.getQualifier());
+ }
+
+ /**
+ * @return whether the resource is a subview (in the view tree)
+ */
+ public static boolean isSubview(Resource resource) {
+ return isSet(resource) && Resource.QUALIFIER_SUBVIEW.equals(resource.getQualifier());
+ }
+
+ /**
+ * @return whether the resource is the root project
+ */
+ public static boolean isRootProject(Resource resource) {
+ return Resource.QUALIFIER_PROJECT.equals(resource.getQualifier());
+ }
+
+ /**
+ * @return whther a resource is a maven module of project
+ */
+ public static boolean isModuleProject(Resource resource) {
+ return Resource.QUALIFIER_MODULE.equals(resource.getQualifier());
+ }
+
+ /**
+ * @return whether a resource is a package
+ */
+ public static boolean isPackage(Resource resource) {
+ return resource != null && Resource.QUALIFIER_PACKAGE.equals(resource.getQualifier());
+ }
+
+
+ /**
+ * @return whether a resource is a set
+ */
+ public static boolean isSet(Resource resource) {
+ return resource != null && Resource.SCOPE_SET.equals(resource.getScope());
+ }
+
+ /**
+ * @return whether a resource is a space
+ */
+ public static boolean isSpace(Resource resource) {
+ return resource != null && Resource.SCOPE_SPACE.equals(resource.getScope());
+ }
+
+ /**
+ * @return whether a resource is an entity.
+ */
+ public static boolean isEntity(Resource resource) {
+ return resource != null && Resource.SCOPE_ENTITY.equals(resource.getScope());
+ }
+
+ /**
+ * This method equal isRootProject(resource) or isModuleProject(resource)
+ */
+ public static boolean isProject(Resource resource) {
+ return isSet(resource);
+ }
+
+ /**
+ * Alias for isDirectory(resource)
+ */
+ public static boolean isDirectory(Resource resource) {
+ return isSpace(resource);
+ }
+
+ /**
+ * Alias for isEntity(resource)
+ */
+ public static boolean isFile(Resource resource) {
+ return isEntity(resource);
+ }
+
+
+ /* QUALIFIERS */
+
+ /**
+ * @return whether a resource is a class
+ */
+ public static boolean isClass(Resource resource) {
+ return Resource.QUALIFIER_CLASS.equals(resource.getQualifier());
+ }
+
+
+ /**
+ * @return whether a resource is a unit test class
+ */
+ public static boolean isUnitTestClass(Resource resource) {
+ return Resource.QUALIFIER_UNIT_TEST_CLASS.equals(resource.getQualifier());
+ }
+
+
+ public static boolean isLibrary(Resource resource) {
+ return Resource.QUALIFIER_LIB.equals(resource.getQualifier());
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/AbstractImportableRulesRepository.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/AbstractImportableRulesRepository.java
new file mode 100644
index 00000000000..9926a33081e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/AbstractImportableRulesRepository.java
@@ -0,0 +1,75 @@
+/*
+ * 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 org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.CharEncoding;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.resources.Language;
+import org.sonar.api.utils.SonarException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+@Deprecated
+public abstract class AbstractImportableRulesRepository<LANG extends Language, MAPPER extends RulePriorityMapper<?, ?>> extends AbstractRulesRepository<LANG, MAPPER> implements ConfigurationImportable {
+
+ public AbstractImportableRulesRepository(LANG language, MAPPER mapper) {
+ super(language, mapper);
+ }
+
+ /**
+ * A map a of profiles to import, The profile name as key, and the xml profile file name in the classpath
+ *
+ * @return
+ */
+ public abstract Map<String, String> getBuiltInProfiles();
+
+ public final List<RulesProfile> getProvidedProfiles() {
+ List<RulesProfile> profiles = new ArrayList<RulesProfile>();
+
+ Map<String, String> defaultProfiles = new TreeMap<String, String>(getBuiltInProfiles());
+ for (Map.Entry<String, String> entry : defaultProfiles.entrySet()) {
+ profiles.add(loadProvidedProfile(entry.getKey(), getCheckResourcesBase() + entry.getValue()));
+ }
+ return profiles;
+ }
+
+ public final RulesProfile loadProvidedProfile(String name, String fileName) {
+ InputStream input = null;
+ try {
+ input = getClass().getResourceAsStream(fileName);
+ RulesProfile profile = new RulesProfile(name, getLanguage().getKey());
+ profile.setActiveRules(importConfiguration(IOUtils.toString(input, CharEncoding.UTF_8), getInitialReferential()));
+ return profile;
+
+ } catch (IOException e) {
+ throw new SonarException("Configuration file not found for the profile : " + name, e);
+
+ } finally {
+ IOUtils.closeQuietly(input);
+ }
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/AbstractRulesRepository.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/AbstractRulesRepository.java
new file mode 100644
index 00000000000..ec69f047174
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/AbstractRulesRepository.java
@@ -0,0 +1,76 @@
+/*
+ * 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 org.apache.commons.io.IOUtils;
+import org.sonar.api.resources.Language;
+import org.sonar.api.utils.SonarException;
+
+import java.io.InputStream;
+import java.util.List;
+
+@Deprecated
+public abstract class AbstractRulesRepository<LANG extends Language, MAPPER extends RulePriorityMapper<?, ?>> implements RulesRepository<LANG> {
+
+ private MAPPER priorityMapper;
+ private LANG language;
+
+ public AbstractRulesRepository(LANG language, MAPPER priorityMapper) {
+ super();
+ this.priorityMapper = priorityMapper;
+ this.language = language;
+ }
+
+ public LANG getLanguage() {
+ return language;
+ }
+
+ public abstract String getRepositoryResourcesBase();
+
+ public final List<Rule> getInitialReferential() {
+ String baseCP = getCheckResourcesBase();
+ InputStream input = getClass().getResourceAsStream(baseCP + "rules.xml");
+ if (input == null) {
+ throw new SonarException("Resource not found : " + baseCP + "rules.xml");
+ }
+ try {
+ return new StandardRulesXmlParser().parse(input);
+ }
+ finally {
+ IOUtils.closeQuietly(input);
+ }
+ }
+
+ public List<Rule> parseReferential(String fileContent) {
+ return new StandardRulesXmlParser().parse(fileContent);
+ }
+
+ public MAPPER getRulePriorityMapper() {
+ return priorityMapper;
+ }
+
+ protected String getCheckResourcesBase() {
+ String base = getRepositoryResourcesBase();
+ base = base.startsWith("/") ? base : "/" + base;
+ base = base.endsWith("/") ? base : base + "/";
+ return base;
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRule.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRule.java
new file mode 100644
index 00000000000..b76cd5e4220
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRule.java
@@ -0,0 +1,228 @@
+/*
+ * 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 org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.Transformer;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.profiles.RulesProfile;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.persistence.*;
+
+/**
+ * A class to map an ActiveRule to the hibernate model
+ */
+@Entity
+@Table(name = "active_rules")
+public class ActiveRule implements Cloneable {
+
+ @Id
+ @Column(name = "id")
+ @GeneratedValue
+ private Integer id;
+
+ @ManyToOne(fetch = FetchType.EAGER)
+ @JoinColumn(name = "rule_id", updatable = true, nullable = false)
+ private Rule rule;
+
+ @Column(name = "failure_level", updatable = true, nullable = false)
+ @Enumerated(EnumType.ORDINAL)
+ private RulePriority priority;
+
+ @ManyToOne(fetch = FetchType.EAGER)
+ @JoinColumn(name = "profile_id", updatable = true, nullable = false)
+ private RulesProfile rulesProfile;
+
+ @OneToMany(mappedBy = "activeRule", fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REMOVE})
+ private List<ActiveRuleParam> activeRuleParams = new ArrayList<ActiveRuleParam>();
+
+ /**
+ * @deprecated visibility should be reduced to protected or package
+ */
+ @Deprecated
+ public ActiveRule() {
+ }
+
+ /**
+ * @deprecated visibility should be reduced to protected or package
+ */
+ @Deprecated
+ public ActiveRule(RulesProfile profile, Rule rule, RulePriority priority) {
+ this.rule = rule;
+ if (priority == null && rule != null) {
+ this.priority = rule.getPriority();
+ } else {
+ this.priority = priority;
+ }
+
+ this.rulesProfile = profile;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ /**
+ * @deprecated visibility should be decreased to protected or package
+ */
+ @Deprecated
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public Rule getRule() {
+ return rule;
+ }
+
+ /**
+ * @deprecated visibility should be reduced to protected or package
+ */
+ @Deprecated
+ public void setRule(Rule rule) {
+ this.rule = rule;
+ }
+
+ public RulePriority getPriority() {
+ return priority;
+ }
+
+ public void setPriority(RulePriority priority) {
+ this.priority = priority;
+ }
+
+ public RulesProfile getRulesProfile() {
+ return rulesProfile;
+ }
+
+ /**
+ * @deprecated visibility should be reduced to protected or package
+ */
+ @Deprecated
+ public void setRulesProfile(RulesProfile rulesProfile) {
+ this.rulesProfile = rulesProfile;
+ }
+
+ public List<ActiveRuleParam> getActiveRuleParams() {
+ return activeRuleParams;
+ }
+
+ /**
+ * @deprecated use setParameter()
+ */
+ @Deprecated
+ public void setActiveRuleParams(List<ActiveRuleParam> params) {
+ this.activeRuleParams = params;
+ }
+
+ public ActiveRule setParameter(String key, String value) {
+ RuleParam ruleParameter = rule.getParam(key);
+ if (ruleParameter != null) {
+ activeRuleParams.add(new ActiveRuleParam(this, ruleParameter, value));
+ }
+ return this;
+ }
+
+ public ActiveRuleParam getParameter(String key) {
+ if (activeRuleParams != null) {
+ for (ActiveRuleParam param : activeRuleParams) {
+ if (StringUtils.equals(key, param.getKey())) {
+ return param;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @deprecated use getRepositoryKey()
+ */
+ @Deprecated
+ public String getPluginName() {
+ return rule.getPluginName();
+ }
+
+ public String getRepositoryKey() {
+ return rule.getRepositoryKey();
+ }
+
+ /**
+ * @return the config key the active rule belongs to
+ */
+ public String getConfigKey() {
+ return rule.getConfigKey();
+ }
+
+ /**
+ * @return the key of the active rule
+ */
+ public String getRuleKey() {
+ return rule.getKey();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ActiveRule that = (ActiveRule) o;
+
+ if (!rule.equals(that.rule)) {
+ return false;
+ }
+ if (rulesProfile != null ? !rulesProfile.equals(that.rulesProfile) : that.rulesProfile != null) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = rule.hashCode();
+ result = 31 * result + (rulesProfile != null ? rulesProfile.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).append("id", getId()).append("rule", rule).append("priority", priority).append("params", activeRuleParams).toString();
+ }
+
+ @Override
+ public Object clone() {
+ ActiveRule clone = new ActiveRule(getRulesProfile(), getRule(), getPriority());
+ if (CollectionUtils.isNotEmpty(getActiveRuleParams())) {
+ clone.setActiveRuleParams(new ArrayList<ActiveRuleParam>(CollectionUtils.collect(getActiveRuleParams(), new Transformer() {
+ public Object transform(Object input) {
+ return ((ActiveRuleParam) input).clone();
+ }
+ })));
+ }
+ return clone;
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRuleParam.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRuleParam.java
new file mode 100644
index 00000000000..f3df8e7e945
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ActiveRuleParam.java
@@ -0,0 +1,145 @@
+/*
+ * 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 org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "active_rule_parameters")
+public class ActiveRuleParam implements Cloneable {
+
+
+ @Id
+ @Column(name = "id")
+ @GeneratedValue
+ private Integer id;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "active_rule_id")
+ private ActiveRule activeRule;
+
+ @ManyToOne(fetch = FetchType.LAZY, optional = true)
+ @JoinColumn(name = "rules_parameter_id")
+ private RuleParam ruleParam;
+
+ @Column(name = "value", updatable = false, nullable = true, length = 4000)
+ private String value;
+
+ public Integer getId() {
+ return id;
+ }
+
+ /**
+ * @deprecated visibility should be decreased to protected or package
+ */
+ @Deprecated
+ void setId(Integer id) {
+ this.id = id;
+ }
+
+ /**
+ * @deprecated visibility should be decreased to protected or package
+ */
+ @Deprecated
+ public ActiveRuleParam() {
+ }
+
+ /**
+ * @deprecated visibility should be decreased to protected or package
+ */
+ @Deprecated
+ public ActiveRuleParam(ActiveRule activeRule, RuleParam ruleParam, String value) {
+ this.activeRule = activeRule;
+ this.ruleParam = ruleParam;
+ this.value = value;
+ }
+
+ public ActiveRule getActiveRule() {
+ return activeRule;
+ }
+
+ /**
+ * @deprecated visibility should be decreased to protected or package
+ */
+ @Deprecated
+ public void setActiveRule(ActiveRule activeRule) {
+ this.activeRule = activeRule;
+ }
+
+ public RuleParam getRuleParam() {
+ return ruleParam;
+ }
+
+ /**
+ * @deprecated visibility should be decreased to protected or package
+ */
+ @Deprecated
+ public void setRuleParam(RuleParam ruleParam) {
+ this.ruleParam = ruleParam;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String getValue(boolean useDefaultValueIfNeeded) {
+ if (value==null && useDefaultValueIfNeeded) {
+ return ruleParam.getDefaultValue();
+ }
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getKey() {
+ return ruleParam.getKey();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof RuleParam)) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ RuleParam other = (RuleParam) obj;
+ return new EqualsBuilder()
+ .append(getId(), other.getKey()).isEquals();
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(17, 37)
+ .append(getId())
+ .toHashCode();
+ }
+
+ @Override
+ public Object clone() {
+ return new ActiveRuleParam(getActiveRule(), getRuleParam(), getValue());
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/ConfigurationExportable.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ConfigurationExportable.java
new file mode 100644
index 00000000000..46c9d17fda4
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ConfigurationExportable.java
@@ -0,0 +1,28 @@
+/*
+ * 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 org.sonar.api.profiles.RulesProfile;
+
+public interface ConfigurationExportable {
+
+ String exportConfiguration(RulesProfile profile);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/ConfigurationImportable.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ConfigurationImportable.java
new file mode 100644
index 00000000000..07e60fd8f34
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ConfigurationImportable.java
@@ -0,0 +1,28 @@
+/*
+ * 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 java.util.List;
+
+public interface ConfigurationImportable {
+
+ List<ActiveRule> importConfiguration(String configuration, List<Rule> rules);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/Iso9126RulesCategories.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/Iso9126RulesCategories.java
new file mode 100644
index 00000000000..88724e603c3
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/Iso9126RulesCategories.java
@@ -0,0 +1,37 @@
+/*
+ * 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 java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public final class Iso9126RulesCategories {
+ private Iso9126RulesCategories() {
+ }
+
+ public static final RulesCategory RELIABILITY = new RulesCategory("Reliability", "The extent to which the project can be expected to perform its intended function with rescission. Some examples : are loop indexes range tested? Is input data checked for range errors ? Is divide-by-zero avoided ? Is exception handling provided ?");
+ public static final RulesCategory EFFICIENCY = new RulesCategory("Efficiency", "The extent to which the project fulfills its purpose without waste of resources. This means resources in the sense of memory utilisation and processor speed.");
+ public static final RulesCategory PORTABILITY = new RulesCategory("Portability", "The extent to which the project can be operated easily and well on multiple computer configurations. Portability can mean both between different hardware setups and between different operating systems -- such as running on both Mac OS X and GNU/Linux.");
+ public static final RulesCategory USABILITY = new RulesCategory("Usability", "The extent to which the project can be understood, learned, operated, attractive and compliant with usability regulations and guidelines. It commonly relies on naming conventions and formatting rules.");
+ public static final RulesCategory MAINTAINABILITY = new RulesCategory("Maintainability", "The extent to which the project facilitates updating to satisfy new requirements. Thus the the project which is maintainable should be not complex.");
+
+ public static final List<RulesCategory> ALL = Collections.unmodifiableList(Arrays.asList(RELIABILITY, EFFICIENCY, PORTABILITY, USABILITY, MAINTAINABILITY));
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java
new file mode 100644
index 00000000000..6af63d2d370
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/Rule.java
@@ -0,0 +1,410 @@
+/*
+ * 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 org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.sonar.api.database.DatabaseProperties;
+
+import javax.persistence.*;
+import java.util.ArrayList;
+import java.util.List;
+
+@Entity
+@Table(name = "rules")
+public final class Rule {
+
+ public static enum Cardinality {
+ SINGLE, MULTIPLE
+ }
+
+ @Id
+ @Column(name = "id")
+ @GeneratedValue
+ private Integer id;
+
+ /**
+ * The default priority given to a rule if not explicitely set
+ */
+ public static final RulePriority DEFAULT_PRIORITY = RulePriority.MAJOR;
+
+ @Column(name = "name", updatable = true, nullable = false)
+ private String name;
+
+ @Column(name = "plugin_rule_key", updatable = false, nullable = true)
+ private String key;
+
+ @Column(name = "enabled", updatable = true, nullable = true)
+ private Boolean enabled = Boolean.TRUE;
+
+ @Column(name = "plugin_config_key", updatable = true, nullable = true)
+ private String configKey;
+
+ @ManyToOne(fetch = FetchType.EAGER)
+ @JoinColumn(name = "rules_category_id", updatable = true, nullable = true)
+ private RulesCategory rulesCategory;
+
+ @Column(name = "priority", updatable = true, nullable = true)
+ @Enumerated(EnumType.ORDINAL)
+ private RulePriority priority = DEFAULT_PRIORITY;
+
+ @Column(name = "description", updatable = true, nullable = true, length = DatabaseProperties.MAX_TEXT_SIZE)
+ private String description;
+
+ @Column(name = "plugin_name", updatable = true, nullable = false)
+ private String pluginName;
+
+ @Enumerated(EnumType.STRING)
+ @Column(name = "cardinality", updatable = true, nullable = false)
+ private Cardinality cardinality = Cardinality.SINGLE;
+
+ @ManyToOne(fetch = FetchType.EAGER)
+ @JoinColumn(name = "parent_id", updatable = true, nullable = true)
+ private Rule parent = null;
+
+ @org.hibernate.annotations.Cascade(
+ {org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN}
+)
+ @OneToMany(mappedBy = "rule")
+ private List<RuleParam> params = new ArrayList<RuleParam>();
+
+ /**
+ * @deprecated since 2.3. Use the factory method create()
+ */
+ @Deprecated
+ public Rule() {
+ // TODO reduce visibility to package
+ }
+
+ /**
+ * Creates rule with minimum set of info
+ *
+ * @param pluginName the plugin name indicates which plugin the rule belongs to
+ * @param key the key should be unique within a plugin, but it is even more careful for the time being that it is unique
+ * across the application
+ * @deprecated since 2.3. Use the factory method create()
+ */
+ @Deprecated
+ public Rule(String pluginName, String key) {
+ this.pluginName = pluginName;
+ this.key = key;
+ this.configKey = key;
+ }
+
+ /**
+ * Creates a fully qualified rule
+ *
+ * @param pluginKey the plugin the rule belongs to
+ * @param key the key should be unique within a plugin, but it is even more careful for the time being that it is unique
+ * across the application
+ * @param name the name displayed in the UI
+ * @param rulesCategory the ISO category the rule belongs to
+ * @param priority this is the priority associated to the rule
+ * @deprecated since 2.3. Use the factory method create()
+ */
+ @Deprecated
+ public Rule(String pluginKey, String key, String name, RulesCategory rulesCategory, RulePriority priority) {
+ setName(name);
+ this.key = key;
+ this.configKey = key;
+ this.rulesCategory = rulesCategory;
+ this.priority = priority;
+ this.pluginName = pluginKey;
+ }
+
+ /**
+ * @deprecated Use the factory method create()
+ */
+ @Deprecated
+ public Rule(String name, String key, RulesCategory rulesCategory, String pluginName, String description) {
+ this();
+ setName(name);
+ this.key = key;
+ this.configKey = key;
+ this.rulesCategory = rulesCategory;
+ this.pluginName = pluginName;
+ this.description = description;
+ }
+
+ /**
+ * @deprecated since 2.3. Use the factory method create()
+ */
+ @Deprecated
+ public Rule(String name, String key, String configKey, RulesCategory rulesCategory, String pluginName, String description) {
+ this();
+ setName(name);
+ this.key = key;
+ this.configKey = configKey;
+ this.rulesCategory = rulesCategory;
+ this.pluginName = pluginName;
+ this.description = description;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ /**
+ * @deprecated visibility should be decreased to protected or package
+ */
+ @Deprecated
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the rule name
+ */
+ public Rule setName(String name) {
+ this.name = removeNewLineCharacters(name);
+ return this;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ /**
+ * Sets the rule key
+ */
+ public Rule setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ /**
+ * @return the rule category
+ */
+ public RulesCategory getRulesCategory() {
+ return rulesCategory;
+ }
+
+ /**
+ * Sets the rule category
+ */
+ public Rule setRulesCategory(RulesCategory rulesCategory) {
+ this.rulesCategory = rulesCategory;
+ return this;
+ }
+
+ public String getPluginName() {
+ return pluginName;
+ }
+
+ /**
+ * Sets the plugin name the rule belongs to
+ */
+ public Rule setPluginName(String pluginName) {
+ this.pluginName = pluginName;
+ return this;
+ }
+
+ public String getConfigKey() {
+ return configKey;
+ }
+
+ /**
+ * Sets the configuration key
+ */
+ public Rule setConfigKey(String configKey) {
+ this.configKey = configKey;
+ return this;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public Boolean isEnabled() {
+ return enabled;
+ }
+
+ /**
+ * Do not call. Used only by sonar.
+ */
+ public Rule setEnabled(Boolean b) {
+ this.enabled = b;
+ return this;
+ }
+
+ /**
+ * Sets the rule description
+ */
+ public Rule setDescription(String description) {
+ this.description = StringUtils.strip(description);
+ return this;
+ }
+
+ public List<RuleParam> getParams() {
+ return params;
+ }
+
+ public RuleParam getParam(String key) {
+ for (RuleParam param : params) {
+ if (StringUtils.equals(key, param.getKey())) {
+ return param;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Sets the rule parameters
+ */
+ public Rule setParams(List<RuleParam> params) {
+ this.params.clear();
+ for (RuleParam param : params) {
+ param.setRule(this);
+ this.params.add(param);
+ }
+ return this;
+ }
+
+ public RuleParam createParameter() {
+ RuleParam parameter = new RuleParam();
+ parameter.setRule(this);
+ params.add(parameter);
+ return parameter;
+ }
+
+ public RuleParam createParameter(String key) {
+ RuleParam parameter = new RuleParam()
+ .setKey(key)
+ .setRule(this);
+ params.add(parameter);
+ return parameter;
+ }
+
+ public Integer getCategoryId() {
+ if (rulesCategory != null) {
+ return rulesCategory.getId();
+ }
+ return null;
+ }
+
+ public RulePriority getPriority() {
+ return priority;
+ }
+
+ /**
+ * Sets the rule priority. If null, uses the default priority
+ */
+ public Rule setPriority(RulePriority priority) {
+ if (priority == null) {
+ this.priority = DEFAULT_PRIORITY;
+ } else {
+ this.priority = priority;
+ }
+
+ return this;
+ }
+
+ public String getRepositoryKey() {
+ return pluginName;
+ }
+
+ public Rule setRepositoryKey(String s) {
+ this.pluginName = s;
+ return this;
+ }
+
+ public Rule setUniqueKey(String repositoryKey, String key) {
+ return setRepositoryKey(repositoryKey).setKey(key);
+ }
+
+ public Cardinality getCardinality() {
+ return cardinality;
+ }
+
+ public Rule setCardinality(Cardinality c) {
+ this.cardinality = c;
+ return this;
+ }
+
+ public Rule getParent() {
+ return parent;
+ }
+
+ public Rule setParent(Rule parent) {
+ this.parent = parent;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Rule)) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ Rule other = (Rule) obj;
+ return new EqualsBuilder()
+ .append(pluginName, other.getPluginName())
+ .append(key, other.getKey())
+ .isEquals();
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(17, 37)
+ .append(pluginName)
+ .append(key)
+ .toHashCode();
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("id", getId())
+ .append("name", name)
+ .append("key", key)
+ .append("configKey", configKey)
+ .append("categ", rulesCategory)
+ .append("plugin", pluginName)
+ .toString();
+ }
+
+ private String removeNewLineCharacters(String text) {
+ String removedCRLF = StringUtils.remove(text, "\n");
+ removedCRLF = StringUtils.remove(removedCRLF, "\r");
+ removedCRLF = StringUtils.remove(removedCRLF, "\n\r");
+ removedCRLF = StringUtils.remove(removedCRLF, "\r\n");
+ return removedCRLF;
+ }
+
+ public static Rule create() {
+ return new Rule();
+ }
+
+ /**
+ * Create with all required fields
+ */
+ public static Rule create(String repositoryKey, String key, String name) {
+ return new Rule().setUniqueKey(repositoryKey, key).setName(name);
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleAnnotationUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleAnnotationUtils.java
new file mode 100644
index 00000000000..27b0b2bd8df
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleAnnotationUtils.java
@@ -0,0 +1,95 @@
+/*
+ * 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 org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.check.AnnotationIntrospector;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * @since 2.3
+ */
+public final class RuleAnnotationUtils {
+
+ private static final Logger LOG = LoggerFactory.getLogger(RuleAnnotationUtils.class);
+
+ private RuleAnnotationUtils() {
+ // only static methods
+ }
+
+ public static List<Rule> readAnnotatedClasses(Collection<Class> annotatedClasses) {
+ List<Rule> rules = new ArrayList<Rule>();
+ if (annotatedClasses != null) {
+ for (Class annotatedClass : annotatedClasses) {
+ Rule rule = readAnnotatedClass(annotatedClass);
+ if (rule != null) {
+ rules.add(rule);
+ }
+ }
+ }
+ return rules;
+ }
+
+ public static Rule readAnnotatedClass(Class annotatedClass) {
+ org.sonar.check.Check checkAnnotation = AnnotationIntrospector.getCheckAnnotation(annotatedClass);
+ if (checkAnnotation == null) {
+ LOG.warn("The class " + annotatedClass.getCanonicalName() + " is not a rule. It should be annotated with " + org.sonar.check.Check.class);
+ return null;
+ }
+
+ Rule rule = toRule(annotatedClass, checkAnnotation);
+ Field[] fields = annotatedClass.getDeclaredFields();
+ if (fields != null) {
+ for (Field field : fields) {
+ createParam(rule, field);
+ }
+ }
+ return rule;
+ }
+
+ private static Rule toRule(Class annotatedClass, org.sonar.check.Check annotation) {
+ String key = AnnotationIntrospector.getCheckKey(annotatedClass);
+
+ Rule rule = Rule.create();
+ rule.setKey(key);
+ rule.setName(annotation.title());
+ rule.setDescription(annotation.description());
+ rule.setRulesCategory(new RulesCategory(annotation.isoCategory().name()));
+ rule.setPriority(RulePriority.fromCheckPriority(annotation.priority()));
+ return rule;
+ }
+
+ private static void createParam(Rule rule, Field field) {
+ org.sonar.check.CheckProperty propertyAnnotation = field.getAnnotation(org.sonar.check.CheckProperty.class);
+ if (propertyAnnotation != null) {
+ String fieldKey = propertyAnnotation.key();
+ if (fieldKey==null || "".equals(fieldKey)) {
+ fieldKey = field.getName();
+ }
+ RuleParam param = rule.createParameter(fieldKey);
+ param.setDescription(propertyAnnotation.description());
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleParam.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleParam.java
new file mode 100644
index 00000000000..32db0d3ea31
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleParam.java
@@ -0,0 +1,162 @@
+/*
+ * 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 org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
+
+import javax.persistence.*;
+
+@Entity
+@Table(name = "rules_parameters")
+public class RuleParam {
+
+ @Id
+ @Column(name = "id")
+ @GeneratedValue
+ private Integer id;
+
+ @ManyToOne(fetch = FetchType.EAGER)
+ @JoinColumn(name = "rule_id")
+ private Rule rule;
+
+ @Column(name = "name", updatable = true, nullable = false, length = 128)
+ private String key;
+
+ @Column(name = "description", updatable = true, nullable = true, length = 4000)
+ private String description;
+
+ @Column(name = "param_type", updatable = true, nullable = true, length = 512)
+ private String type;
+
+ @Column(name = "default_value", updatable = true, nullable = true, length = 4000)
+ private String defaultValue;
+
+ /**
+ * @deprecated since 2.3 use the factory method Rule.setParameter()
+ */
+ @Deprecated
+ public RuleParam() {
+ }
+
+ /**
+ * @deprecated since 2.3 use the factory method setParameter()
+ */
+ @Deprecated
+ public RuleParam(Rule rule, String key, String description, String type) {
+ this.rule = rule;
+ this.key = key;
+ this.description = description;
+ this.type = type;
+ }
+
+ public Rule getRule() {
+ return rule;
+ }
+
+ RuleParam setRule(Rule rule) {
+ this.rule = rule;
+ return this;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public RuleParam setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public RuleParam setDescription(String s) {
+ this.description = StringUtils.defaultString(s, "");
+ return this;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public RuleParam setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ public String getDefaultValue() {
+ return defaultValue;
+ }
+
+ public Boolean getDefaultValueAsBoolean() {
+ if (defaultValue!=null) {
+ return Boolean.parseBoolean(defaultValue);
+ }
+ return null;
+ }
+
+ public Integer getDefaultValueAsInteger() {
+ if (defaultValue!=null) {
+ return Integer.parseInt(defaultValue);
+ }
+ return null;
+ }
+
+ public RuleParam setDefaultValue(String s) {
+ this.defaultValue = s;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof RuleParam)) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ RuleParam other = (RuleParam) obj;
+ return new EqualsBuilder()
+ //.append(rule, other.getRule())
+ .append(key, other.getKey()).isEquals();
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(17, 37)
+ //.append(rule)
+ .append(key)
+ .toHashCode();
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("id", id)
+ .append("key", key)
+ .append("desc", description)
+ .append("type", type)
+ .toString();
+ }
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulePriority.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulePriority.java
new file mode 100644
index 00000000000..cd2b00afa8a
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulePriority.java
@@ -0,0 +1,94 @@
+/*
+ * 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 org.sonar.check.Priority;
+
+/**
+ * A class to hold rules priority
+ */
+public enum RulePriority {
+
+ /**
+ * WARNING : DO NOT CHANGE THE ENUMERATION ORDER
+ * the enum ordinal is used for db persistence
+ */
+ INFO, MINOR, MAJOR, CRITICAL, BLOCKER;
+
+ /**
+ * A class to map priority level prior to Sonar 1.10 to the new ones
+ *
+ * @param level an old priority level : Error or Warning
+ * @return the corresponding RulePriority
+ */
+ public static RulePriority valueOfString(String level) {
+ try {
+ return RulePriority.valueOf(level.toUpperCase());
+
+ } catch (IllegalArgumentException ex) {
+ // backward compatibility
+ if (level.equalsIgnoreCase("ERROR")) {
+ return RulePriority.MAJOR;
+ } else if (level.equalsIgnoreCase("WARNING")) {
+ return RulePriority.INFO;
+ }
+ }
+ throw new IllegalArgumentException("Unknown priority " + level);
+ }
+
+
+ public static RulePriority fromCheckPriority(Priority checkPriority) {
+ if (checkPriority == Priority.BLOCKER) {
+ return RulePriority.BLOCKER;
+ }
+ if (checkPriority == Priority.CRITICAL) {
+ return RulePriority.CRITICAL;
+ }
+ if (checkPriority == Priority.MAJOR) {
+ return RulePriority.MAJOR;
+ }
+ if (checkPriority == Priority.MINOR) {
+ return RulePriority.MINOR;
+ }
+ if (checkPriority == Priority.INFO) {
+ return RulePriority.INFO;
+ }
+ return null;
+ }
+
+ public Priority toCheckPriority() {
+ if (this == BLOCKER) {
+ return Priority.BLOCKER;
+ }
+ if (this == CRITICAL) {
+ return Priority.CRITICAL;
+ }
+ if (this == MAJOR) {
+ return Priority.MAJOR;
+ }
+ if (this == MINOR) {
+ return Priority.MINOR;
+ }
+ if (this == INFO) {
+ return Priority.INFO;
+ }
+ return null;
+ }
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulePriorityMapper.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulePriorityMapper.java
new file mode 100644
index 00000000000..570c951561f
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulePriorityMapper.java
@@ -0,0 +1,28 @@
+/*
+ * 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;
+
+public interface RulePriorityMapper<IN_TYPE, OUT_TYPE> {
+
+ RulePriority from(IN_TYPE priority);
+
+ OUT_TYPE to(RulePriority priority);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleProvider.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleProvider.java
new file mode 100644
index 00000000000..99e4ac55882
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleProvider.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.rules;
+
+import org.sonar.api.BatchComponent;
+import org.sonar.api.ServerComponent;
+
+import java.util.Collection;
+
+/**
+ * @since 2.3
+ */
+public interface RuleProvider extends BatchComponent, ServerComponent {
+
+ Rule findByKey(String repositoryKey, String key);
+ Rule find(RuleQuery query);
+ Collection<Rule> findAll(RuleQuery query);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleQuery.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleQuery.java
new file mode 100644
index 00000000000..a3443c952ff
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleQuery.java
@@ -0,0 +1,64 @@
+/*
+ * 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;
+
+/**
+ * @since 2.3
+ */
+public final class RuleQuery {
+
+ private String repositoryKey = null;
+ private String key = null;
+ private String configKey = null;
+
+ RuleQuery() {
+ }
+
+ public static RuleQuery create() {
+ return new RuleQuery();
+ }
+
+ public RuleQuery withRepositoryKey(String s) {
+ this.repositoryKey = s;
+ return this;
+ }
+
+ public RuleQuery withKey(String s) {
+ this.key = s;
+ return this;
+ }
+
+ public RuleQuery withConfigKey(String s) {
+ this.configKey = s;
+ return this;
+ }
+
+ public String getRepositoryKey() {
+ return repositoryKey;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getConfigKey() {
+ return configKey;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleRepository.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleRepository.java
new file mode 100644
index 00000000000..3c21c975026
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleRepository.java
@@ -0,0 +1,84 @@
+/*
+ * 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 org.apache.commons.lang.StringUtils;
+import org.sonar.api.ServerExtension;
+
+import java.util.List;
+
+/**
+ * @since 2.3
+ */
+public abstract class RuleRepository implements ServerExtension {
+
+ private String key;
+ private String language;
+ private String name;
+
+ protected RuleRepository(String key, String language) {
+ this.key = key;
+ this.language = language;
+ }
+
+ public final String getKey() {
+ return key;
+ }
+
+ public final String getLanguage() {
+ return language;
+ }
+
+ public final String getName() {
+ return name;
+ }
+
+ public final String getName(boolean useKeyIfEmpty) {
+ if (useKeyIfEmpty) {
+ return StringUtils.defaultIfEmpty(name, key);
+ }
+ return name;
+ }
+
+ public final RuleRepository setName(String s) {
+ this.name = s;
+ return this;
+ }
+
+ public abstract List<Rule> createRules();
+
+
+ @Override
+ public final boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ RuleRepository that = (RuleRepository)o;
+ return key.equals(that.key);
+ }
+
+ @Override
+ public final int hashCode() {
+ return key.hashCode();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleUtils.java
new file mode 100644
index 00000000000..5fc9ae3edc7
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RuleUtils.java
@@ -0,0 +1,55 @@
+/*
+ * 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 java.util.Map;
+
+import org.apache.commons.configuration.Configuration;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.utils.KeyValueFormat;
+
+/**
+ * A utility class to manipulate concepts around rules
+ */
+public final class RuleUtils {
+
+ private RuleUtils() {
+ }
+
+ /**
+ * Gets a Map<RulePriority, Integer> containing the weights defined in the settings
+ * Default value is used when the property is not set (see property key and default value in the class CoreProperties)
+ *
+ * @param configuration the Sonar configuration
+ * @return a map
+ */
+ public static Map<RulePriority, Integer> getPriorityWeights(final Configuration configuration) {
+ String levelWeight = configuration.getString(CoreProperties.CORE_RULE_WEIGHTS_PROPERTY, CoreProperties.CORE_RULE_WEIGHTS_DEFAULT_VALUE);
+
+ Map<RulePriority, Integer> weights = KeyValueFormat.parse(levelWeight, new KeyValueFormat.RulePriorityNumbersPairTransformer());
+
+ for (RulePriority priority : RulePriority.values()) {
+ if (!weights.containsKey(priority)) {
+ weights.put(priority, 1);
+ }
+ }
+ return weights;
+ }
+}
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
new file mode 100644
index 00000000000..08a5343f842
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesCategory.java
@@ -0,0 +1,154 @@
+/*
+ * 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 org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.hibernate.annotations.Cache;
+import org.hibernate.annotations.CacheConcurrencyStrategy;
+import org.hibernate.annotations.Immutable;
+import org.sonar.api.database.BaseIdentifiable;
+import org.sonar.check.IsoCategory;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+/**
+ * A class to hold rules category
+ */
+@Immutable
+@Cache(usage = CacheConcurrencyStrategy.READ_ONLY)
+@Entity
+@Table(name = "rules_categories")
+public class RulesCategory extends BaseIdentifiable {
+
+ @Column(name = "name", updatable = false, nullable = false)
+ private String name;
+
+ @Column(name = "description", updatable = false, nullable = true)
+ private String description;
+
+ /**
+ * Creates a RuleCategory based on the category name
+ *
+ * @param name the category name
+ */
+ public RulesCategory(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Creates a category based on the category name and description
+ *
+ * @param name the category name
+ * @param description the category description
+ */
+ public RulesCategory(String name, String description) {
+ this.name = name;
+ this.description = description;
+ }
+
+ /**
+ * Creates an empty category
+ */
+ public RulesCategory() {
+ }
+
+ /**
+ * @return the category name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the category name
+ *
+ * @param name the name
+ */
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * @return the category description
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Sets the cay description
+ * @param description the description
+ */
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof RulesCategory)) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ RulesCategory other = (RulesCategory) obj;
+ return new EqualsBuilder()
+ .append(name, other.getName()).isEquals();
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(17, 37)
+ .append(name)
+ .toHashCode();
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this)
+ .append("id", getId())
+ .append("name", name)
+ .append("desc", description)
+ .toString();
+ }
+
+ public IsoCategory toIsoCategory() {
+ if (name.equals(Iso9126RulesCategories.EFFICIENCY.getName())) {
+ return IsoCategory.Efficiency;
+ }
+ if (name.equals(Iso9126RulesCategories.MAINTAINABILITY.getName())) {
+ return IsoCategory.Maintainability;
+ }
+ if (name.equals(Iso9126RulesCategories.PORTABILITY.getName())) {
+ return IsoCategory.Portability;
+ }
+ if (name.equals(Iso9126RulesCategories.RELIABILITY.getName())) {
+ return IsoCategory.Reliability;
+ }
+ if (name.equals(Iso9126RulesCategories.USABILITY.getName())) {
+ return IsoCategory.Usability;
+ }
+ return null;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesManager.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesManager.java
new file mode 100644
index 00000000000..1f87fabe09e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesManager.java
@@ -0,0 +1,114 @@
+/*
+ * 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 org.sonar.api.Plugin;
+import org.sonar.api.resources.Language;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Manage and access rules defined in Sonar.
+ *
+ * @deprecated UGLY COMPONENT - WILL BE COMPLETELY REFACTORED IN SONAR 2.3
+ */
+@Deprecated
+public abstract class RulesManager {
+
+ /**
+ * Returns the list of languages for which there is a rule repository
+ *
+ * @return a Set of languages
+ */
+ public abstract Set<Language> getLanguages();
+
+ /**
+ * Gets the list of Rules Repositories available for a language
+ *
+ * @param language the language
+ * @return the list of rules repositories
+ */
+ public abstract List<RulesRepository<?>> getRulesRepositories(Language language);
+
+ /**
+ * Gets the complete list of Rules Repositories in the Sonar instance
+ *
+ * @return the list of rules repositories
+ */
+ public abstract List<RulesRepository<?>> getRulesRepositories();
+
+ /**
+ * Gets the list of rules plugins for a given language
+ * @param language the language
+ * @return the list of plugins
+ */
+ public abstract List<Plugin> getPlugins(Language language);
+
+ /**
+ * Gets count of rules by categories defined for a given language
+ *
+ * @param language the language
+ * @return a Map with the category as key and the count as value
+ */
+ public abstract Map<String, Long> countRulesByCategory(Language language);
+
+
+ /**
+ * Get the list of rules plugin that implement a mechanism of export for a given language
+ *
+ * @param language the language
+ * @return the list of plugins
+ */
+ public abstract List<Plugin> getExportablePlugins(Language language);
+ /**
+ * Get the list of rules plugin that implement a mechanism of import for a given language
+ *
+ * @param language the language
+ * @return the list of plugins
+ */
+ public abstract List<Plugin> getImportablePlugins(Language language);
+
+ /**
+ * Gets a list of rules indexed by their key for a given plugin
+ * @param pluginKey the plugin key
+ * @return a Map with the rule key and the rule
+ */
+ public abstract Map<String, Rule> getPluginRulesIndexedByKey(String pluginKey);
+
+ /**
+ * Gets a collection of rules belonging to a plugin
+ *
+ * @param pluginKey the plugin key
+ * @return the collection of rules
+ */
+ public abstract Collection<Rule> getPluginRules(String pluginKey);
+
+ /**
+ * Gets a rule belonging to a defined plugin based on its key
+ *
+ * @param pluginKey the plugin key
+ * @param ruleKey the rule key
+ * @return the rule
+ */
+ public abstract Rule getPluginRule(String pluginKey, String ruleKey);
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesRepository.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesRepository.java
new file mode 100644
index 00000000000..32d035b042e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/RulesRepository.java
@@ -0,0 +1,60 @@
+/*
+ * 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 org.sonar.api.BatchExtension;
+import org.sonar.api.ServerExtension;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.resources.Language;
+
+import java.util.List;
+
+/**
+ * The interface to implement to create a repository of rules
+ *
+ * @param <LANG>
+ */
+@Deprecated
+public interface RulesRepository<LANG extends Language> extends BatchExtension, ServerExtension {
+
+ /**
+ * @return the language the repository is associated
+ */
+ LANG getLanguage();
+
+ /**
+ * @return the list of rules of the repository
+ */
+ List<Rule> getInitialReferential();
+
+ /**
+ * The method to parse the base referential of rules and return a list of rules
+ *
+ * @param fileContent the initial referential
+ * @return a list of rules
+ */
+ List<Rule> parseReferential(String fileContent);
+
+ /**
+ * @return a list of profiles that are provided with the referential
+ */
+ List<RulesProfile> getProvidedProfiles();
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/StandardProfileXmlParser.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/StandardProfileXmlParser.java
new file mode 100644
index 00000000000..5dec9920fe1
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/StandardProfileXmlParser.java
@@ -0,0 +1,118 @@
+/*
+ * 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.thoughtworks.xstream.XStream;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.rules.xml.Profile;
+import org.sonar.api.rules.xml.Property;
+import org.sonar.api.utils.SonarException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class StandardProfileXmlParser {
+
+ private final List<Rule> rules;
+
+ public StandardProfileXmlParser() {
+ rules = new ArrayList<Rule>();
+ }
+
+ public StandardProfileXmlParser(List<Rule> rules) {
+ this.rules = rules;
+ }
+
+ /**
+ * see the XML format into the unit test src/test/java/.../StandardProfileXmlParserTest
+ */
+ public Profile parse(String xml) {
+ return (Profile) getXStream().fromXML(xml);
+ }
+
+ private XStream getXStream() {
+ XStream xstream = new XStream();
+ xstream.processAnnotations(Profile.class);
+ xstream.processAnnotations(Rule.class);
+ xstream.processAnnotations(Property.class);
+ return xstream;
+ }
+
+ public RulesProfile importConfiguration(String configuration) {
+ RulesProfile rulesProfile = new RulesProfile();
+ List<ActiveRule> activeRules = new ArrayList<ActiveRule>();
+ Profile profile = buildProfileFromXml(configuration);
+
+ rulesProfile.setName(profile.getName());
+ rulesProfile.setLanguage(profile.getLanguage());
+
+ if (StringUtils.isBlank(rulesProfile.getName())) {
+ throw new SonarException("Profile name can't be null or empty");
+ }
+
+ buildActiveRulesFromProfile(profile, activeRules);
+ rulesProfile.setActiveRules(activeRules);
+ return rulesProfile;
+ }
+
+ protected Profile buildProfileFromXml(String configuration) {
+ StandardProfileXmlParser xstream = new StandardProfileXmlParser();
+ return xstream.parse(configuration);
+ }
+
+ protected void buildActiveRulesFromProfile(Profile profile, List<ActiveRule> activeRules) {
+ if (profile.getRules() != null && !profile.getRules().isEmpty()) {
+ for (org.sonar.api.rules.xml.Rule module : profile.getRules()) {
+ String ref = module.getKey();
+ for (Rule rule : rules) {
+ if (rule.getConfigKey().equals(ref)) {
+ RulePriority rulePriority = getRulePriority(module);
+ ActiveRule activeRule = new ActiveRule(null, rule, rulePriority);
+ activeRule.setActiveRuleParams(getActiveRuleParams(module, rule, activeRule));
+ activeRules.add(activeRule);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private RulePriority getRulePriority(org.sonar.api.rules.xml.Rule module) {
+ return StringUtils.isBlank(module.getPriority()) ? null : RulePriority.valueOfString(module.getPriority());
+ }
+
+ private List<ActiveRuleParam> getActiveRuleParams(org.sonar.api.rules.xml.Rule module, Rule rule, ActiveRule activeRule) {
+ List<ActiveRuleParam> activeRuleParams = new ArrayList<ActiveRuleParam>();
+ if (module.getProperties() != null) {
+ for (Property property : module.getProperties()) {
+ if (rule.getParams() != null) {
+ for (RuleParam ruleParam : rule.getParams()) {
+ if (ruleParam.getKey().equals(property.getName())) {
+ activeRuleParams.add(new ActiveRuleParam(activeRule, ruleParam, property.getValue()));
+ }
+ }
+ }
+ }
+ }
+ return activeRuleParams;
+ }
+
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/StandardRuleXmlFormat.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/StandardRuleXmlFormat.java
new file mode 100644
index 00000000000..1437f8baa1d
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/StandardRuleXmlFormat.java
@@ -0,0 +1,192 @@
+/*
+ * 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 org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.CharEncoding;
+import org.apache.commons.lang.StringUtils;
+import org.codehaus.stax2.XMLInputFactory2;
+import org.codehaus.staxmate.SMInputFactory;
+import org.codehaus.staxmate.in.SMHierarchicCursor;
+import org.codehaus.staxmate.in.SMInputCursor;
+import org.sonar.api.utils.SonarException;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLStreamException;
+
+/**
+ * @since 2.3
+ */
+public final class StandardRuleXmlFormat {
+
+ private StandardRuleXmlFormat() {
+ // only static methods
+ }
+
+ public static List<Rule> parseXml(File file) {
+ Reader reader = null;
+ try {
+ reader = new InputStreamReader(FileUtils.openInputStream(file), CharEncoding.UTF_8);
+ return parseXml(reader);
+
+ } catch (IOException e) {
+ throw new SonarException("Fail to load the file: " + file, e);
+
+ } finally {
+ IOUtils.closeQuietly(reader);
+ }
+ }
+
+ /**
+ * Warning : the input stream is closed in this method
+ */
+ public static List<Rule> parseXml(InputStream input) {
+ Reader reader = null;
+ try {
+ reader = new InputStreamReader(input, CharEncoding.UTF_8);
+ return parseXml(reader);
+
+ } catch (IOException e) {
+ throw new SonarException("Fail to load the xml stream", e);
+
+ } finally {
+ IOUtils.closeQuietly(reader);
+ }
+ }
+
+ public static List<Rule> parseXml(Reader reader) {
+ XMLInputFactory xmlFactory = XMLInputFactory2.newInstance();
+ xmlFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE);
+ xmlFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, Boolean.FALSE);
+ // just so it won't try to load DTD in if there's DOCTYPE
+ xmlFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
+ xmlFactory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE);
+ SMInputFactory inputFactory = new SMInputFactory(xmlFactory);
+ try {
+ SMHierarchicCursor rootC = inputFactory.rootElementCursor(reader);
+ rootC.advance(); // <rules>
+ List<Rule> rules = new ArrayList<Rule>();
+
+ SMInputCursor rulesC = rootC.childElementCursor("rule");
+ while (rulesC.getNext() != null) {
+ // <rule>
+ Rule rule = Rule.create();
+ rules.add(rule);
+
+ processRule(rule, rulesC);
+ }
+ return rules;
+
+ } catch (XMLStreamException e) {
+ throw new SonarException("XML is not valid", e);
+ }
+ }
+
+ private static void processRule(Rule rule, SMInputCursor ruleC) throws XMLStreamException {
+ /* BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT */
+ String keyAttribute = ruleC.getAttrValue("key");
+ if (StringUtils.isNotBlank(keyAttribute)) {
+ rule.setKey(StringUtils.trim(keyAttribute));
+ }
+
+ /* BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT */
+ String priorityAttribute = ruleC.getAttrValue("priority");
+ if (StringUtils.isNotBlank(priorityAttribute)) {
+ rule.setPriority(RulePriority.valueOf(StringUtils.trim(priorityAttribute)));
+ }
+
+ SMInputCursor cursor = ruleC.childElementCursor();
+
+ while (cursor.getNext() != null) {
+ String nodeName = cursor.getLocalName();
+
+ if (StringUtils.equalsIgnoreCase("name", nodeName)) {
+ rule.setName(StringUtils.trim(cursor.collectDescendantText(false)));
+
+ } else if (StringUtils.equalsIgnoreCase("description", nodeName)) {
+ rule.setDescription(StringUtils.trim(cursor.collectDescendantText(false)));
+
+ } else if (StringUtils.equalsIgnoreCase("key", nodeName)) {
+ rule.setKey(StringUtils.trim(cursor.collectDescendantText(false)));
+
+ } else if (StringUtils.equalsIgnoreCase("configKey", nodeName)) {
+ rule.setConfigKey(StringUtils.trim(cursor.collectDescendantText(false)));
+
+ } else if (StringUtils.equalsIgnoreCase("priority", nodeName)) {
+ rule.setPriority(RulePriority.valueOf(StringUtils.trim(cursor.collectDescendantText(false))));
+
+ } else if (StringUtils.equalsIgnoreCase("category", nodeName)) {
+ /* BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT : attribute "name" */
+ String category = StringUtils.trim(StringUtils.defaultString(cursor.getAttrValue("name"), cursor.collectDescendantText(false)));
+ rule.setRulesCategory(new RulesCategory(category));
+
+ } else if (StringUtils.equalsIgnoreCase("cardinality", nodeName)) {
+ rule.setCardinality(Rule.Cardinality.valueOf(StringUtils.trim(cursor.collectDescendantText(false))));
+
+ } else if (StringUtils.equalsIgnoreCase("param", nodeName)) {
+ processParameter(rule, cursor);
+ }
+ }
+ if (StringUtils.isEmpty(rule.getKey())) {
+ throw new SonarException("Node <key> is missing in <rule>");
+ }
+ if (StringUtils.isEmpty(rule.getName())) {
+ throw new SonarException("Node <name> is missing in <rule>");
+ }
+ }
+
+ private static void processParameter(Rule rule, SMInputCursor ruleC) throws XMLStreamException {
+ RuleParam param = rule.createParameter();
+
+ String keyAttribute = ruleC.getAttrValue("key");
+ if (StringUtils.isNotBlank(keyAttribute)) {
+ /* BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT */
+ param.setKey(StringUtils.trim(keyAttribute));
+ }
+
+ String typeAttribute = ruleC.getAttrValue("type");
+ if (StringUtils.isNotBlank(typeAttribute)) {
+ /* BACKWARD COMPATIBILITY WITH DEPRECATED FORMAT */
+ param.setType(StringUtils.trim(typeAttribute));
+ }
+
+ SMInputCursor paramC = ruleC.childElementCursor();
+ while (paramC.getNext() != null) {
+ String propNodeName = paramC.getLocalName();
+ String propText = StringUtils.trim(paramC.collectDescendantText(false));
+ if (StringUtils.equalsIgnoreCase("key", propNodeName)) {
+ param.setKey(propText);
+
+ } else if (StringUtils.equalsIgnoreCase("description", propNodeName)) {
+ param.setDescription(propText);
+
+ } else if (StringUtils.equalsIgnoreCase("type", propNodeName)) {
+ param.setType(propText);
+ }
+ }
+ if (StringUtils.isEmpty(param.getKey())) {
+ throw new SonarException("Node <key> is missing in <param>");
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/StandardRulesXmlParser.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/StandardRulesXmlParser.java
new file mode 100644
index 00000000000..e532df1d040
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/StandardRulesXmlParser.java
@@ -0,0 +1,160 @@
+/*
+ * 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.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
+import com.thoughtworks.xstream.core.util.QuickWriter;
+import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
+import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
+import com.thoughtworks.xstream.io.xml.XppDriver;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.CharEncoding;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.utils.SonarException;
+
+import java.io.*;
+import java.lang.ref.WeakReference;
+import java.util.*;
+
+@Deprecated
+public class StandardRulesXmlParser {
+
+ /**
+ * see the XML format into the unit test src/test/java/.../StandardRulesXmlParserTest
+ */
+ public List<Rule> parse(String xml) {
+ InputStream inputStream = null;
+ try {
+ inputStream = IOUtils.toInputStream(xml, CharEncoding.UTF_8);
+ return setDefaultRulePriorities((List<Rule>) getXStream().fromXML(inputStream));
+
+ } catch (IOException e) {
+ throw new SonarException("Can't parse xml file", e);
+
+ } finally {
+ IOUtils.closeQuietly(inputStream);
+ }
+ }
+
+ public List<Rule> parse(Reader reader) {
+ return setDefaultRulePriorities((List<Rule>) getXStream().fromXML(reader));
+ }
+
+ public List<Rule> parse(InputStream input) {
+ try {
+ return setDefaultRulePriorities((List<Rule>) getXStream().fromXML(new InputStreamReader(input, CharEncoding.UTF_8)));
+
+ } catch (UnsupportedEncodingException e) {
+ throw new SonarException("Can't parse xml file", e);
+ }
+ }
+
+ private List<Rule> setDefaultRulePriorities(List<Rule> rules) {
+ for (Rule rule : rules) {
+ if (rule.getPriority() == null) {
+ rule.setPriority(RulePriority.MAJOR);
+ }
+ }
+ return rules;
+ }
+
+ public String toXml(List<Rule> rules) {
+ return getXStream().toXML(rules);
+ }
+
+ private static class CDataXppDriver extends XppDriver {
+ @Override
+ public HierarchicalStreamWriter createWriter(Writer out) {
+ return new XDataPrintWriter(out);
+ }
+ }
+
+ private static class XDataPrintWriter extends PrettyPrintWriter {
+ public XDataPrintWriter(Writer writer) {
+ super(writer);
+ }
+
+ @Override
+ protected void writeText(QuickWriter writer, String text) {
+ writer.write("<![CDATA[");
+ writer.write(text);
+ writer.write("]]>");
+ }
+ }
+
+ private XStream getXStream() {
+ XStream xstream = new XStream(new CDataXppDriver());
+ xstream.registerConverter(new TrimStringConverter());
+ xstream.alias("rules", ArrayList.class);
+
+ xstream.alias("categ", RulesCategory.class);
+ xstream.useAttributeFor(RulesCategory.class, "name");
+ xstream.aliasField("category", Rule.class, "rulesCategory");
+
+ xstream.alias("rule", Rule.class);
+ xstream.useAttributeFor(Rule.class, "key");
+ xstream.useAttributeFor("priority", RulePriority.class);
+
+ xstream.addImplicitCollection(Rule.class, "params");
+
+ xstream.alias("param", RuleParam.class);
+ xstream.useAttributeFor(RuleParam.class, "key");
+ xstream.useAttributeFor(RuleParam.class, "type");
+
+ // only for backward compatibility with sonar 1.4.
+ xstream.omitField(RuleParam.class, "defaultValue");
+ return xstream;
+ }
+
+ /**
+ * See http://svn.codehaus.org/xstream/trunk/xstream/src/java/com/thoughtworks/xstream/converters/basic/StringConverter.java
+ */
+ public static class TrimStringConverter extends AbstractSingleValueConverter {
+
+ private final Map cache;
+
+ public TrimStringConverter(final Map map) {
+ cache = map;
+ }
+
+ public TrimStringConverter() {
+ this(Collections.synchronizedMap(new WeakHashMap()));
+ }
+
+ public boolean canConvert(final Class type) {
+ return type.equals(String.class);
+ }
+
+ public Object fromString(final String str) {
+ String trim = StringUtils.trim(str);
+ final WeakReference ref = (WeakReference) cache.get(trim);
+ String s = (String) (ref == null ? null : ref.get());
+
+ if (s == null) {
+ // fill cache
+ cache.put(str, new WeakReference(trim));
+ s = trim;
+ }
+
+ return s;
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/Violation.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/Violation.java
new file mode 100644
index 00000000000..b7e83e6f2ef
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/Violation.java
@@ -0,0 +1,153 @@
+/*
+ * 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 org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ReflectionToStringBuilder;
+import org.sonar.api.resources.Resource;
+
+/**
+ * A class that represents a violation. A violation happens when a resource does not respect a defined rule.
+ */
+public class Violation {
+
+ private Resource resource;
+ private Rule rule;
+ private String message;
+ private RulePriority priority;
+ private Integer lineId;
+
+ /**
+ * Creates of a violation from a rule. Will need to define the resource later on
+ */
+ public Violation(Rule rule) {
+ this.rule = rule;
+ }
+
+ /**
+ * Creates a fully qualified violation
+ *
+ * @param rule the rule that has been violated
+ * @param resource the resource the violation should be attached to
+ */
+ public Violation(Rule rule, Resource resource) {
+ this.resource = resource;
+ this.rule = rule;
+ }
+
+ public Resource getResource() {
+ return resource;
+ }
+
+ /**
+ * Sets the resource the violation applies to
+ *
+ * @return the current object
+ */
+ public Violation setResource(Resource resource) {
+ this.resource = resource;
+ return this;
+ }
+
+ public Rule getRule() {
+ return rule;
+ }
+
+ /**
+ * Sets the rule violated
+ *
+ * @return the current object
+ */
+ public Violation setRule(Rule rule) {
+ this.rule = rule;
+ return this;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * Sets the violation message
+ *
+ * @return the current object
+ */
+ public Violation setMessage(String message) {
+ this.message = message;
+ return this;
+ }
+
+ public Integer getLineId() {
+ return lineId;
+ }
+
+ /**
+ * Sets the violation line
+ *
+ * @return the current object
+ */
+ public Violation setLineId(Integer lineId) {
+ this.lineId = lineId;
+ return this;
+ }
+
+ public RulePriority getPriority() {
+ return priority;
+ }
+
+ /**
+ * Sets the violation priority
+ *
+ * @return the current object
+ */
+ public Violation setPriority(RulePriority priority) {
+ this.priority = priority;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Violation)) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ Violation other = (Violation) obj;
+ return new EqualsBuilder()
+ .append(rule, other.getRule())
+ .append(resource, other.getResource())
+ .isEquals();
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(17, 37)
+ .append(getRule())
+ .append(getResource())
+ .toHashCode();
+ }
+
+ @Override
+ public String toString() {
+ return ReflectionToStringBuilder.toString(this);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/ViolationFilter.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ViolationFilter.java
new file mode 100644
index 00000000000..78d001a1a47
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/ViolationFilter.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.rules;
+
+import org.sonar.api.BatchExtension;
+
+/**
+ * Filter violations to save. For example, ignore a violation if it occurs on a line of code commented with //NOSONAR
+ *
+ * @since 1.12
+ */
+public interface ViolationFilter extends BatchExtension {
+
+ /**
+ * Return true if the violation must be ignored, else it's saved into database.
+ */
+ boolean isIgnored(Violation violation);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/xml/Profile.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/xml/Profile.java
new file mode 100644
index 00000000000..8ec21f61004
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/xml/Profile.java
@@ -0,0 +1,64 @@
+/*
+ * 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.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+import com.thoughtworks.xstream.annotations.XStreamImplicit;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@XStreamAlias("profile")
+public class Profile {
+
+ @XStreamAsAttribute
+ private String language;
+
+ @XStreamAsAttribute
+ private String name;
+
+ @XStreamImplicit
+ private List<Rule> rules = new ArrayList<Rule>();
+
+ public List<Rule> getRules() {
+ return rules;
+ }
+
+ public void setRules(List<Rule> rules) {
+ this.rules = rules;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/xml/Property.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/xml/Property.java
new file mode 100644
index 00000000000..eb338f1ab82
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/xml/Property.java
@@ -0,0 +1,46 @@
+/*
+ * 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.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+
+@XStreamAlias("property")
+public class Property {
+
+ @XStreamAsAttribute
+ private String name;
+
+ @XStreamAsAttribute
+ private String value;
+
+ public Property(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/rules/xml/Rule.java b/sonar-plugin-api/src/main/java/org/sonar/api/rules/xml/Rule.java
new file mode 100644
index 00000000000..8f008aacaec
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/rules/xml/Rule.java
@@ -0,0 +1,81 @@
+/*
+ * 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.xml;
+
+import com.thoughtworks.xstream.annotations.XStreamAlias;
+import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
+import com.thoughtworks.xstream.annotations.XStreamImplicit;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@XStreamAlias("rule")
+public class Rule implements Comparable<String> {
+
+ @XStreamAsAttribute
+ private String key;
+
+ @XStreamAsAttribute
+ private String priority;
+
+ @XStreamImplicit
+ private List<Property> properties;
+
+ public Rule(String ref) {
+ this(ref, null);
+ }
+
+ public Rule(String ref, String priority) {
+ this.key = ref;
+ this.priority = priority;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setProperties(List<Property> properties) {
+ this.properties = properties;
+ }
+
+ public List<Property> getProperties() {
+ return properties;
+ }
+
+ public int compareTo(String o) {
+ return o.compareTo(key);
+ }
+
+ public String getPriority() {
+ return priority;
+ }
+
+ public void setPriority(String priority) {
+ this.priority = priority;
+ }
+
+ public void addProperty(Property property) {
+ if (properties == null) {
+ properties = new ArrayList<Property>();
+ }
+ properties.add(property);
+ }
+
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/security/GroupRole.java b/sonar-plugin-api/src/main/java/org/sonar/api/security/GroupRole.java
new file mode 100644
index 00000000000..2493ac0b7c3
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/security/GroupRole.java
@@ -0,0 +1,84 @@
+/*
+ * 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.security;
+
+import org.sonar.api.database.BaseIdentifiable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+/**
+ * @since 1.12
+ */
+@Entity
+@Table(name = "group_roles")
+public class GroupRole extends BaseIdentifiable {
+
+ public static final Integer ANYONE_GROUP_ID = null;
+
+ @Column(name = "group_id")
+ private Integer groupId;
+
+ @Column(name = "role")
+ private String role;
+
+ @Column(name = "resource_id")
+ private Integer resourceId;
+
+ public static GroupRole buildGlobalRole(Integer groupId, String role) {
+ return new GroupRole().setGroupId(groupId).setRole(role);
+ }
+
+ public static GroupRole buildResourceRole(Integer groupId, String role, Integer resourceId) {
+ return new GroupRole().setGroupId(groupId).setRole(role).setResourceId(resourceId);
+ }
+
+ public Integer getGroupId() {
+ return groupId;
+ }
+
+ public GroupRole setGroupId(Integer groupId) {
+ this.groupId = groupId;
+ return this;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public GroupRole setRole(String role) {
+ this.role = role;
+ return this;
+ }
+
+ public Integer getResourceId() {
+ return resourceId;
+ }
+
+ public GroupRole setResourceId(Integer resourceId) {
+ this.resourceId = resourceId;
+ return this;
+ }
+
+ public boolean isAnyone() {
+ return groupId==ANYONE_GROUP_ID;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/security/LoginPasswordAuthenticator.java b/sonar-plugin-api/src/main/java/org/sonar/api/security/LoginPasswordAuthenticator.java
new file mode 100644
index 00000000000..069042f048f
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/security/LoginPasswordAuthenticator.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.security;
+
+import org.sonar.api.ServerExtension;
+
+/**
+ * @since 1.12
+ */
+public interface LoginPasswordAuthenticator extends ServerExtension {
+
+ /**
+ * Throws a runtime exception if the authenticator can not be initialized at sonar server startup, eg. if the connection to LDAP server is refused.
+ */
+ void init();
+
+ boolean authenticate(String login, String password);
+
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/security/UserRole.java b/sonar-plugin-api/src/main/java/org/sonar/api/security/UserRole.java
new file mode 100644
index 00000000000..15e42b57087
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/security/UserRole.java
@@ -0,0 +1,81 @@
+/*
+ * 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.security;
+
+import org.sonar.api.database.BaseIdentifiable;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+/**
+ * This JPA model maps the table user_roles
+ *
+ * @since 1.12
+ */
+@Entity
+@Table(name = "user_roles")
+public class UserRole extends BaseIdentifiable {
+
+ @Column(name = "user_id")
+ private Integer userId;
+
+ @Column(name = "role")
+ private String role;
+
+ @Column(name = "resource_id")
+ private Integer resourceId;
+
+ public UserRole(Integer userId, String role, Integer resourceId) {
+ this.userId = userId;
+ this.role = role;
+ this.resourceId = resourceId;
+ }
+
+ public UserRole() {
+ }
+
+ public Integer getUserId() {
+ return userId;
+ }
+
+ public UserRole setUserId(Integer userId) {
+ this.userId = userId;
+ return this;
+ }
+
+ public String getRole() {
+ return role;
+ }
+
+ public UserRole setRole(String role) {
+ this.role = role;
+ return this;
+ }
+
+ public Integer getResourceId() {
+ return resourceId;
+ }
+
+ public UserRole setResourceId(Integer resourceId) {
+ this.resourceId = resourceId;
+ return this;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/AnnotationUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/AnnotationUtils.java
new file mode 100644
index 00000000000..b31b2cb4a85
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/AnnotationUtils.java
@@ -0,0 +1,47 @@
+/*
+ * 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.utils;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * A utility class for annotations
+ * @since 1.11
+ */
+public final class AnnotationUtils {
+
+ private AnnotationUtils() {
+ }
+
+ /**
+ * Searches for a class annotation. All inheritance tree is analysed.
+ */
+ public static <A> A getClassAnnotation(final Object object, final Class<A> annotationClass) {
+ Class aClass = object.getClass();
+ while (aClass != null) {
+ Annotation annotation = aClass.getAnnotation(annotationClass);
+ if (annotation != null) {
+ return (A) annotation;
+ }
+ aClass = aClass.getSuperclass();
+ }
+ return null;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/HttpDownloader.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/HttpDownloader.java
new file mode 100644
index 00000000000..ab56f296d26
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/HttpDownloader.java
@@ -0,0 +1,110 @@
+/*
+ * 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.utils;
+
+import org.apache.commons.io.IOUtils;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.BatchComponent;
+import org.sonar.api.ServerComponent;
+import org.sonar.api.platform.Server;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URI;
+
+/**
+ * Simple class to download a file from a HTTP repository.
+ *
+ * @since 2.2
+ */
+public class HttpDownloader implements BatchComponent, ServerComponent {
+
+ public static final int TIMEOUT_MILLISECONDS = 20 * 1000;
+
+ private Server server = null;
+
+ public HttpDownloader(Server server) {
+ this.server = server;
+ }
+
+ public HttpDownloader() {
+ }
+
+ public void download(URI uri, File toFile) {
+ InputStream input = null;
+ FileOutputStream output = null;
+ try {
+ HttpURLConnection connection = newHttpConnection(uri);
+ output = new FileOutputStream(toFile, false);
+ input = connection.getInputStream();
+ IOUtils.copy(input, output);
+
+ } catch (Exception e) {
+ throw new SonarException("Fail to download the file: " + uri, e);
+
+ } finally {
+ IOUtils.closeQuietly(input);
+ IOUtils.closeQuietly(output);
+ }
+ }
+
+ public byte[] download(URI uri) {
+ InputStream input = null;
+ try {
+ HttpURLConnection connection = newHttpConnection(uri);
+ input = connection.getInputStream();
+ return IOUtils.toByteArray(input);
+
+ } catch (Exception e) {
+ throw new SonarException("Fail to download the file: " + uri, e);
+
+ } finally {
+ IOUtils.closeQuietly(input);
+ }
+ }
+
+ public InputStream openStream(URI uri) {
+ try {
+ HttpURLConnection connection = newHttpConnection(uri);
+ return connection.getInputStream();
+
+ } catch (Exception e) {
+ throw new SonarException("Fail to download the file: " + uri, e);
+ }
+ }
+
+ private HttpURLConnection newHttpConnection(URI uri) throws IOException {
+ LoggerFactory.getLogger(getClass()).info("Download: " + uri);
+ HttpURLConnection connection = (HttpURLConnection) uri.toURL().openConnection();
+ connection.setConnectTimeout(TIMEOUT_MILLISECONDS);
+ connection.setReadTimeout(TIMEOUT_MILLISECONDS);
+ connection.setUseCaches(true);
+ connection.setRequestProperty("User-Agent", getUserAgent());
+ connection.setInstanceFollowRedirects(true);
+ return connection;
+ }
+
+ private String getUserAgent() {
+ return (server != null ? "Sonar " + server.getVersion() : "Sonar");
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/IocContainer.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/IocContainer.java
new file mode 100644
index 00000000000..87bb6b2c035
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/IocContainer.java
@@ -0,0 +1,54 @@
+/*
+ * 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.utils;
+
+import org.picocontainer.Characteristics;
+import org.picocontainer.DefaultPicoContainer;
+import org.picocontainer.MutablePicoContainer;
+import org.picocontainer.behaviors.OptInCaching;
+import org.picocontainer.lifecycle.ReflectionLifecycleStrategy;
+import org.picocontainer.monitors.NullComponentMonitor;
+
+
+/**
+ * Proxy to inject the container as a component$
+ *
+ * @since 1.10
+ */
+public class IocContainer {
+ private final MutablePicoContainer pico;
+
+ public IocContainer(MutablePicoContainer pico) {
+ this.pico = pico;
+ }
+
+ public MutablePicoContainer getPicoContainer() {
+ return pico;
+ }
+
+ public static MutablePicoContainer buildPicoContainer() {
+ ReflectionLifecycleStrategy lifecycleStrategy = new ReflectionLifecycleStrategy(new
+ NullComponentMonitor(), "start", "stop", "dispose");
+
+ DefaultPicoContainer result = new DefaultPicoContainer(new OptInCaching(), lifecycleStrategy, null);
+ result.as(Characteristics.CACHE).addComponent(new IocContainer(result)); // for components that directly inject other components (eg Plugins)
+ return result;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/KeyValue.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/KeyValue.java
new file mode 100644
index 00000000000..ebfd9b603a0
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/KeyValue.java
@@ -0,0 +1,72 @@
+/*
+ * 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.utils;
+
+/**
+ * A utility class to store a key / value couple of generic types
+ *
+ * @since 1.10
+ */
+public class KeyValue<KEY, VALUE> {
+
+ private KEY key;
+ private VALUE value;
+
+ /**
+ * Creates a key / value object
+ */
+ public KeyValue(KEY key, VALUE value) {
+ super();
+ this.key = key;
+ this.value = value;
+ }
+
+ /**
+ * @return the key of the couple
+ */
+ public KEY getKey() {
+ return key;
+ }
+
+ /**
+ * Sets the key of the couple
+ *
+ * @param key the key
+ */
+ public void setKey(KEY key) {
+ this.key = key;
+ }
+
+ /**
+ *
+ * @return the value of the couple
+ */
+ public VALUE getValue() {
+ return value;
+ }
+
+ /**
+ * Sets the value of the couple
+ */
+ public void setValue(VALUE value) {
+ this.value = value;
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/KeyValueFormat.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/KeyValueFormat.java
new file mode 100644
index 00000000000..4b5c34feba0
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/KeyValueFormat.java
@@ -0,0 +1,241 @@
+/*
+ * 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.utils;
+
+import com.google.common.collect.Multiset;
+import org.apache.commons.collections.Bag;
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.math.NumberUtils;
+import org.sonar.api.rules.RulePriority;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Util class to format key/value data. Output is a string representation ready to be
+ * injected into the database
+ *
+ * @since 1.10
+ */
+public final class KeyValueFormat {
+
+ private KeyValueFormat() {
+ }
+
+ /**
+ * Transforms a string with the following format : "key1=value1;key2=value2..."
+ * into a Map<KEY, VALUE>. Requires to implement the transform(key,value) method
+ *
+ * @param data the input string
+ * @param transformer the interface to implement
+ * @return a Map of <key, value>
+ */
+ public static <KEY, VALUE> Map<KEY, VALUE> parse(String data, Transformer<KEY, VALUE> transformer) {
+ Map<String, String> rawData = parse(data);
+ Map<KEY, VALUE> map = new HashMap<KEY, VALUE>();
+ for (Map.Entry<String, String> entry : rawData.entrySet()) {
+ KeyValue<KEY, VALUE> keyVal = transformer.transform(entry.getKey(), entry.getValue());
+ if (keyVal != null) {
+ map.put(keyVal.getKey(), keyVal.getValue());
+ }
+ }
+ return map;
+ }
+
+ /**
+ * Transforms a string with the following format : "key1=value1;key2=value2..."
+ * into a Map<String,String>
+ *
+ * @param data the string to parse
+ * @return a map
+ */
+ public static Map<String, String> parse(String data) {
+ Map<String, String> map = new HashMap<String, String>();
+ String[] pairs = StringUtils.split(data, ";");
+ for (String pair : pairs) {
+ String[] keyValue = StringUtils.split(pair, "=");
+ String key = keyValue[0];
+ String value = (keyValue.length == 2 ? keyValue[1] : "");
+ map.put(key, value);
+ }
+ return map;
+ }
+
+ /**
+ * Transforms a map<KEY,VALUE> into a string with the format : "key1=value1;key2=value2..."
+ *
+ * @param map the map to transform
+ * @return the formatted string
+ */
+ public static <KEY, VALUE> String format(Map<KEY, VALUE> map) {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (Map.Entry<?, ?> entry : map.entrySet()) {
+ if (!first) {
+ sb.append(";");
+ }
+ sb.append(entry.getKey().toString());
+ sb.append("=");
+ if (entry.getValue() != null) {
+ sb.append(entry.getValue());
+ }
+ first = false;
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * @since 1.11
+ * @deprecated use Multiset from google collections instead of commons-collections bags
+ */
+ public static String format(Bag bag) {
+ return format(bag, 0);
+ }
+
+ /**
+ * @since 1.11
+ * @deprecated use Multiset from google collections instead of commons-collections bags
+ */
+ public static String format(Bag bag, int var) {
+ StringBuilder sb = new StringBuilder();
+ if (bag != null) {
+ boolean first = true;
+ for (Object obj : bag.uniqueSet()) {
+ if (!first) {
+ sb.append(";");
+ }
+ sb.append(obj.toString());
+ sb.append("=");
+ sb.append(bag.getCount(obj) + var);
+ first = false;
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Transforms a Multiset<?> into a string with the format : "key1=count1;key2=count2..."
+ *
+ * @param set the set to transform
+ * @return the formatted string
+ */
+ public static String format(Multiset<?> set) {
+ StringBuilder sb = new StringBuilder();
+ if (set != null) {
+ boolean first = true;
+ for (Multiset.Entry<?> entry : set.entrySet()) {
+ if (!first) {
+ sb.append(";");
+ }
+ sb.append(entry.getElement().toString());
+ sb.append("=");
+ sb.append(entry.getCount());
+ first = false;
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Transforms a Object... into a string with the format : "object1=object2;object3=object4..."
+ *
+ * @param objects the object list to transform
+ * @return the formatted string
+ */
+ public static String format(Object... objects) {
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ if (objects != null) {
+ for (int i = 0; i < objects.length; i++) {
+ if (!first) {
+ sb.append(";");
+ }
+ sb.append(objects[i++].toString());
+ sb.append("=");
+ sb.append(objects[i]);
+ first = false;
+ }
+ }
+ return sb.toString();
+ }
+
+ public interface Transformer<KEY, VALUE> {
+ KeyValue<KEY, VALUE> transform(String key, String value);
+ }
+
+ /**
+ * Implementation of Transformer<String, Double>
+ */
+ public static class StringNumberPairTransformer implements Transformer<String, Double> {
+
+ public KeyValue<String, Double> transform(String key, String value) {
+ return new KeyValue<String, Double>(key, toDouble(value));
+ }
+ }
+
+ /**
+ * Implementation of Transformer<Double, Double>
+ */
+ public static class DoubleNumbersPairTransformer implements Transformer<Double, Double> {
+
+ public KeyValue<Double, Double> transform(String key, String value) {
+ return new KeyValue<Double, Double>(toDouble(key), toDouble(value));
+ }
+ }
+
+ /**
+ * Implementation of Transformer<Integer, Integer>
+ */
+ public static class IntegerNumbersPairTransformer implements Transformer<Integer, Integer> {
+
+ public KeyValue<Integer, Integer> transform(String key, String value) {
+ return new KeyValue<Integer, Integer>(toInteger(key), toInteger(value));
+ }
+ }
+
+ /**
+ * Implementation of Transformer<RulePriority, Integer>
+ */
+ public static class RulePriorityNumbersPairTransformer implements Transformer<RulePriority, Integer> {
+
+ public KeyValue<RulePriority, Integer> transform(String key, String value) {
+ try {
+ if (StringUtils.isBlank(value)) {
+ value = "0";
+ }
+ return new KeyValue<RulePriority, Integer>(RulePriority.valueOf(key.toUpperCase()), Integer.parseInt(value));
+ }
+ catch (Exception e) {
+ LoggerFactory.getLogger(RulePriorityNumbersPairTransformer.class).warn("Property " + key + " has invalid value: " + value, e);
+ return null;
+ }
+ }
+ }
+
+ private static Double toDouble(String value) {
+ return StringUtils.isBlank(value) ? null : NumberUtils.toDouble(value);
+ }
+
+ private static Integer toInteger(String value) {
+ return StringUtils.isBlank(value) ? null : NumberUtils.toInt(value);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/LocalizedMessages.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/LocalizedMessages.java
new file mode 100644
index 00000000000..c438f2c58b7
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/LocalizedMessages.java
@@ -0,0 +1,123 @@
+/*
+ * 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.utils;
+
+import com.google.common.collect.Iterators;
+import com.google.common.collect.Lists;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+public class LocalizedMessages extends ResourceBundle {
+
+ private static final Logger LOG = LoggerFactory.getLogger(LocalizedMessages.class);
+
+ private Locale locale;
+ private List<ResourceBundle> bundles;
+
+ /**
+ * Constructs a resource bundle from a list of other resource bundles. If
+ * there are duplicate keys, the key from the resource bundle with the
+ * smallest index takes precedence.
+ */
+ public LocalizedMessages(Locale locale, String... basenames) {
+ this.locale = locale;
+ bundles = new ArrayList<ResourceBundle>(basenames.length);
+ for (String basename : basenames) {
+ bundles.add(getBundle("sonar.bundles." + basename, locale));
+ }
+ }
+
+ public Locale getLocale() {
+ return locale;
+ }
+
+ public String format(String key, Object... args) {
+ return format(true, key, args);
+ }
+
+ public String formatQuietly(String key, Object... args) {
+ return format(false, key, args);
+ }
+
+ private String format(boolean logIfMissing, String key, Object... args) {
+ try {
+ String message = getString(key);
+ return String.format(locale, message, args);
+
+ } catch (MissingResourceException e) {
+ if (logIfMissing) {
+ LOG.warn("Missing translation: key==" + key + ",locale=" + locale);
+ }
+ return key;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.ResourceBundle#getKeys()
+ */
+
+ @Override
+ public Enumeration<String> getKeys() {
+ return new Enumeration<String>() {
+ private Set<String> keys = new HashSet<String>();
+
+ // Set iterator to simulate enumeration
+ private Iterator<String> i;
+
+ // Constructor
+ {
+ for (ResourceBundle b : bundles) {
+ keys.addAll(Lists.newArrayList(Iterators.forEnumeration(b.getKeys())));
+ }
+ i = keys.iterator();
+ }
+
+ public boolean hasMoreElements() {
+ return i.hasNext();
+ }
+
+ public String nextElement() {
+ return i.next();
+ }
+ };
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.util.ResourceBundle#handleGetObject(java.lang.String)
+ */
+
+ @Override
+ protected Object handleGetObject(String key) {
+ for (ResourceBundle b : bundles) {
+ try {
+ return b.getObject(key);
+ } catch (MissingResourceException mre) {
+ // iterate
+ }
+ }
+ throw new MissingResourceException(null, null, key);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/Logs.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/Logs.java
new file mode 100644
index 00000000000..8154c5dbfd4
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/Logs.java
@@ -0,0 +1,39 @@
+/*
+ * 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.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Predefined SLF4j loggers
+ *
+ * @since 1.12
+ */
+public final class Logs {
+
+ private Logs() {
+ }
+
+ /**
+ * This logger is always activated with level INFO
+ */
+ public static final Logger INFO = LoggerFactory.getLogger("org.sonar.INFO");
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/ManifestUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/ManifestUtils.java
new file mode 100644
index 00000000000..d1130e26267
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/ManifestUtils.java
@@ -0,0 +1,60 @@
+/*
+ * 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.utils;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+/**
+ * @since 2.2
+ */
+public final class ManifestUtils {
+
+ private ManifestUtils() {
+ }
+
+ /**
+ * Search for a property in all the manifests found in the classloader
+ *
+ * @return the values, an empty list if the property is not found.
+ */
+ public static List<String> getPropertyValues(ClassLoader classloader, String key) {
+ List<String> values = new ArrayList<String>();
+ try {
+ Enumeration<URL> resources = classloader.getResources("META-INF/MANIFEST.MF");
+ while (resources.hasMoreElements()) {
+ Manifest manifest = new Manifest(resources.nextElement().openStream());
+ Attributes attributes = manifest.getMainAttributes();
+ String value = attributes.getValue(key);
+ if (value != null) {
+ values.add(value);
+ }
+ }
+ } catch (IOException e) {
+ throw new SonarException("Fail to load manifests from classloader: " + classloader, e);
+ }
+ return values;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/ParsingUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/ParsingUtils.java
new file mode 100644
index 00000000000..6e154a59b0b
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/ParsingUtils.java
@@ -0,0 +1,74 @@
+/*
+ * 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.utils;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Locale;
+
+/**
+ * Utility to parse various inputs
+ *
+ * @since 1.10
+ */
+public final class ParsingUtils {
+
+ private ParsingUtils() {
+ }
+
+ /**
+ * Parses a string with a locale and returns the corresponding number
+ *
+ * @throws ParseException if number cannot be parsed
+ */
+ public static double parseNumber(String number, Locale locale) throws ParseException {
+ if ("".equals(number)) {
+ return Double.NaN;
+ }
+ return NumberFormat.getNumberInstance(locale).parse(number).doubleValue();
+ }
+
+ /**
+ * Parses a string with the default locale and returns the corresponding number
+ *
+ * @throws ParseException if number cannot be parsed
+ */
+ public static double parseNumber(String number) throws ParseException {
+ return parseNumber(number, Locale.getDefault());
+ }
+
+ /**
+ * Scales a double value, taking into account 2 decimals
+ */
+ public static double scaleValue(double value) {
+ return scaleValue(value, 2);
+ }
+
+ /**
+ * Scales a double value with decimals
+ */
+ public static double scaleValue(double value, int decimals) {
+ BigDecimal bd = BigDecimal.valueOf(value);
+ return bd.setScale(decimals, RoundingMode.HALF_UP).doubleValue();
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/ServerHttpClient.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/ServerHttpClient.java
new file mode 100644
index 00000000000..9ab2a186513
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/ServerHttpClient.java
@@ -0,0 +1,162 @@
+/*
+ * 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.utils;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.BatchComponent;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+/**
+ * @since 1.10
+ * @deprecated use org.sonar.api.plaform.Server instead
+ */
+@Deprecated
+public class ServerHttpClient implements BatchComponent {
+
+ protected static final String SERVER_API_PATH = "/api/server";
+ private static final String KEY_PATH = SERVER_API_PATH + "/key";
+ private static final String VERSION_PATH = SERVER_API_PATH + "/version";
+ protected static final String MAVEN_PATH = "/deploy/maven";
+ private static final int CONNECT_TIMEOUT_MILLISECONDS = 30000;
+ private static final int READ_TIMEOUT_MILLISECONDS = 60000;
+
+ private String url;
+ private Integer connectTimeoutMiliseconds = CONNECT_TIMEOUT_MILLISECONDS;
+ private Integer readTimeoutMiliseconds = READ_TIMEOUT_MILLISECONDS;
+
+ public ServerHttpClient(String remoteServerUrl) {
+ this(remoteServerUrl, null, null);
+ }
+
+ public ServerHttpClient(String remoteServerUrl, Integer connectTimeoutMiliseconds, Integer readTimeoutMiliseconds) {
+ this.url = StringUtils.chomp(remoteServerUrl, "/");
+ if (connectTimeoutMiliseconds != null) {
+ this.connectTimeoutMiliseconds = connectTimeoutMiliseconds;
+ }
+ if (readTimeoutMiliseconds != null) {
+ this.readTimeoutMiliseconds = readTimeoutMiliseconds;
+ }
+
+ }
+
+ public ServerHttpClient(Configuration configuration) {
+ this(configuration.getString("sonar.host.url", "http://localhost:9000"),
+ configuration.getInteger("sonar.host.connectTimeoutMs", CONNECT_TIMEOUT_MILLISECONDS),
+ configuration.getInteger("sonar.host.readTimeoutMs", READ_TIMEOUT_MILLISECONDS));
+
+ }
+
+ /**
+ * Throws a runtime ServerConnectionException if it fails to connect Sonar server
+ */
+ public void checkUp() {
+ String exceptionLabel = "Sonar server at " + url +
+ " is unreacheable. Either start it or setup the sonar.host.url maven setting if the URL is incorrect";
+ if (getId() == null) {
+ throw new ServerConnectionException(exceptionLabel);
+ }
+ }
+
+ public String getId() {
+ return executeAction(KEY_PATH);
+ }
+
+ public String getVersion() {
+ return executeAction(VERSION_PATH);
+ }
+
+ public String getMavenRepositoryUrl() {
+ return this.url + MAVEN_PATH;
+ }
+
+ protected String executeAction(String action) {
+ String result = getRemoteContent(url + action);
+ if (result.trim().length() == 0) {
+ throw new ServerApiEmptyContentException("Empty " + action + " returned from server");
+ }
+ return result;
+ }
+
+ protected String getRemoteContent(String url) {
+ HttpURLConnection conn = null;
+ Reader reader = null;
+ try {
+ conn = getConnection(url, "GET");
+ reader = new InputStreamReader((InputStream) conn.getContent());
+
+ int statusCode = conn.getResponseCode();
+ if (statusCode != HttpURLConnection.HTTP_OK) {
+ throw new ServerConnectionException("Status returned by url : '" + url + "' is invalid : " + statusCode);
+ }
+
+ return IOUtils.toString(reader);
+ } catch (IOException e) {
+ throw new ServerConnectionException("url=" + url, e);
+
+ } finally {
+ IOUtils.closeQuietly(reader);
+ if (conn != null) {
+ conn.disconnect();
+ }
+ }
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ private HttpURLConnection getConnection(String url, String method) throws IOException {
+ URL page = new URL(url);
+ HttpURLConnection conn = (HttpURLConnection) page.openConnection();
+ conn.setConnectTimeout(connectTimeoutMiliseconds);
+ conn.setReadTimeout(readTimeoutMiliseconds);
+
+ conn.setRequestMethod(method);
+ conn.connect();
+ return conn;
+ }
+
+ public static class ServerApiEmptyContentException extends SonarException {
+
+ public ServerApiEmptyContentException(String s) {
+ super(s);
+ }
+ }
+
+ public static class ServerConnectionException extends SonarException {
+
+ public ServerConnectionException(String msg) {
+ super(msg);
+ }
+
+ public ServerConnectionException(String msg, Throwable throwable) {
+ super(msg, throwable);
+ }
+
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/SonarException.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/SonarException.java
new file mode 100644
index 00000000000..44a00f3c774
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/SonarException.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.utils;
+
+/**
+ * Because we don't like checked exceptions !
+ * @since 1.10
+ */
+public class SonarException extends RuntimeException {
+ public SonarException() {
+ }
+
+ public SonarException(String s) {
+ super(s);
+ }
+
+ public SonarException(String s, Throwable throwable) {
+ super(s, throwable);
+ }
+
+ public SonarException(Throwable throwable) {
+ super(throwable);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/StaxParser.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/StaxParser.java
new file mode 100644
index 00000000000..e210f59abed
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/StaxParser.java
@@ -0,0 +1,204 @@
+/*
+ * 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.utils;
+
+import com.ctc.wstx.stax.WstxInputFactory;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.StringUtils;
+import org.codehaus.stax2.XMLInputFactory2;
+import org.codehaus.staxmate.SMInputFactory;
+import org.codehaus.staxmate.in.SMHierarchicCursor;
+
+import java.io.*;
+import java.net.URL;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.stream.XMLResolver;
+import javax.xml.stream.XMLStreamException;
+
+/**
+ * @since 1.10
+ */
+public class StaxParser {
+
+ private SMInputFactory inf;
+ private XmlStreamHandler streamHandler;
+ private boolean isoControlCharsAwareParser;
+
+ /**
+ * Stax parser for a given stream handler and iso control chars set awarness to off
+ *
+ * @param streamHandler the xml stream handler
+ */
+ public StaxParser(XmlStreamHandler streamHandler) {
+ this(streamHandler, false);
+ }
+
+ /**
+ * Stax parser for a given stream handler and iso control chars set awarness to on.
+ * The iso control chars in the xml file will be replaced by simple spaces, usefull for
+ * potentially bogus XML files to parse, this has a small perfs overhead so use it only when necessary
+ *
+ * @param streamHandler the xml stream handler
+ * @param isoControlCharsAwareParser true or false
+ */
+ public StaxParser(XmlStreamHandler streamHandler, boolean isoControlCharsAwareParser) {
+ this.streamHandler = streamHandler;
+ XMLInputFactory xmlFactory = XMLInputFactory2.newInstance();
+ if (xmlFactory instanceof WstxInputFactory) {
+ WstxInputFactory wstxInputfactory = (WstxInputFactory) xmlFactory;
+ wstxInputfactory.configureForLowMemUsage();
+ wstxInputfactory.getConfig().setUndeclaredEntityResolver(new UndeclaredEntitiesXMLResolver());
+ }
+ xmlFactory.setProperty(XMLInputFactory.IS_VALIDATING, false);
+ xmlFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false);
+ xmlFactory.setProperty(XMLInputFactory.IS_NAMESPACE_AWARE, false);
+ this.isoControlCharsAwareParser = isoControlCharsAwareParser;
+ inf = new SMInputFactory(xmlFactory);
+ }
+
+ public void parse(File xmlFile) throws XMLStreamException {
+ FileInputStream input=null;
+ try {
+ input = new FileInputStream(xmlFile);
+ parse(input);
+ } catch (FileNotFoundException e) {
+ throw new XMLStreamException(e);
+ } finally {
+ IOUtils.closeQuietly(input);
+ }
+ }
+
+ public void parse(InputStream xmlInput) throws XMLStreamException {
+ xmlInput = isoControlCharsAwareParser ? new ISOControlCharAwareInputStream(xmlInput) : xmlInput;
+ parse(inf.rootElementCursor(xmlInput));
+ }
+
+ public void parse(Reader xmlReader) throws XMLStreamException {
+ if (isoControlCharsAwareParser) {
+ throw new SonarException("Method call not supported when isoControlCharsAwareParser=true");
+ }
+ parse(inf.rootElementCursor(xmlReader));
+ }
+
+ public void parse(URL xmlUrl) throws XMLStreamException {
+ try {
+ parse(xmlUrl.openStream());
+ } catch (IOException e) {
+ throw new XMLStreamException(e);
+ }
+ }
+
+ private void parse(SMHierarchicCursor rootCursor) throws XMLStreamException {
+ try {
+ streamHandler.stream(rootCursor);
+ } finally {
+ rootCursor.getStreamReader().closeCompletely();
+ }
+ }
+
+ private static class UndeclaredEntitiesXMLResolver implements XMLResolver {
+ public Object resolveEntity(String arg0, String arg1, String fileName, String undeclaredEntity) throws XMLStreamException {
+ // avoid problems with XML docs containing undeclared entities.. return the entity under its raw form if not an unicode expression
+ if (StringUtils.startsWithIgnoreCase(undeclaredEntity, "u") && undeclaredEntity.length() == 5) {
+ int unicodeCharHexValue = Integer.parseInt(undeclaredEntity.substring(1), 16);
+ if (Character.isDefined(unicodeCharHexValue)) {
+ undeclaredEntity = new String(new char[]{(char) unicodeCharHexValue});
+ }
+ }
+ return undeclaredEntity;
+ }
+ }
+
+ /**
+ * Simple interface for handling XML stream to parse
+ */
+ public interface XmlStreamHandler {
+ void stream(SMHierarchicCursor rootCursor) throws XMLStreamException;
+ }
+
+ private static class ISOControlCharAwareInputStream extends InputStream {
+
+ private InputStream inputToCheck;
+
+ public ISOControlCharAwareInputStream(InputStream inputToCheck) {
+ super();
+ this.inputToCheck = inputToCheck;
+ }
+
+ @Override
+ public int read() throws IOException {
+ return inputToCheck.read();
+ }
+
+ @Override
+ public int available() throws IOException {
+ return inputToCheck.available();
+ }
+
+ @Override
+ public void close() throws IOException {
+ inputToCheck.close();
+ }
+
+ @Override
+ public synchronized void mark(int readlimit) {
+ inputToCheck.mark(readlimit);
+ }
+
+ @Override
+ public boolean markSupported() {
+ return inputToCheck.markSupported();
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ int readen = inputToCheck.read(b, off, len);
+ checkBufferForISOControlChars(b, off, len);
+ return readen;
+ }
+
+ @Override
+ public int read(byte[] b) throws IOException {
+ int readen = inputToCheck.read(b);
+ checkBufferForISOControlChars(b, 0, readen);
+ return readen;
+ }
+
+ @Override
+ public synchronized void reset() throws IOException {
+ inputToCheck.reset();
+ }
+
+ @Override
+ public long skip(long n) throws IOException {
+ return inputToCheck.skip(n);
+ }
+
+ private void checkBufferForISOControlChars(byte[] buffer, int off, int len) {
+ for (int i = off; i < len; i++) {
+ char streamChar = (char) buffer[i];
+ if (Character.isISOControl(streamChar) && streamChar != '\n') {
+ // replace control chars by a simple space
+ buffer[i] = ' ';
+ }
+ }
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/TempFileUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/TempFileUtils.java
new file mode 100644
index 00000000000..6b8ec3ea8c9
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/TempFileUtils.java
@@ -0,0 +1,43 @@
+/*
+ * 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.utils;
+
+import java.io.File;
+import java.io.IOException;
+
+public final class TempFileUtils {
+
+ private TempFileUtils() {
+ // only static methods
+ }
+
+ public static File createTempDirectory() throws IOException {
+ final File temp = File.createTempFile("temp", Long.toString(System.nanoTime()));
+ if (!(temp.delete())) {
+ throw new IOException("Could not delete temp file: " + temp.getAbsolutePath());
+ }
+
+ if (!(temp.mkdir())) {
+ throw new IOException("Could not create temp directory: " + temp.getAbsolutePath());
+ }
+ return temp;
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/TimeProfiler.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/TimeProfiler.java
new file mode 100644
index 00000000000..000d0c9f06d
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/TimeProfiler.java
@@ -0,0 +1,75 @@
+/*
+ * 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.utils;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A very simple profiler to log the time elapsed performing some tasks.
+ * This implementation is not thread-safe.
+ *
+ * @since 2.0
+ */
+public class TimeProfiler {
+
+ private Logger logger;
+ private long start = 0;
+ private String name;
+
+ public TimeProfiler(Logger logger) {
+ this.logger = logger;
+ }
+
+ public TimeProfiler(Class clazz) {
+ this.logger = LoggerFactory.getLogger(clazz);
+ }
+
+ /**
+ * Use the default Sonar logger
+ */
+ public TimeProfiler() {
+ this.logger = Logs.INFO;
+ }
+
+ public TimeProfiler start(String name) {
+ this.name = name;
+ this.start = System.currentTimeMillis();
+ logger.info(name + "...");
+ return this;
+ }
+
+ public TimeProfiler setLogger(Logger logger) {
+ this.logger = logger;
+ return this;
+ }
+
+ public Logger getLogger() {
+ return logger;
+ }
+
+ public TimeProfiler stop() {
+ if (start > 0) {
+ logger.info("{} done: {} ms", name, (System.currentTimeMillis() - start));
+ }
+ start = 0;
+ return this;
+ }
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/ValidationMessages.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/ValidationMessages.java
new file mode 100644
index 00000000000..bff9074bd12
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/ValidationMessages.java
@@ -0,0 +1,85 @@
+/*
+ * 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.utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class ValidationMessages {
+
+ private List<Message> errors = new ArrayList<Message>();
+ private List<Message> warnings = new ArrayList<Message>();
+ private List<Message> infos = new ArrayList<Message>();
+
+ ValidationMessages() {
+ }
+
+ public static ValidationMessages create() {
+ return new ValidationMessages();
+ }
+
+ public boolean hasErrors() {
+ return !errors.isEmpty();
+ }
+ public List<Message> getErrors() {
+ return errors;
+ }
+
+ public List<Message> getWarnings() {
+ return warnings;
+ }
+
+ public List<Message> getInfos() {
+ return infos;
+ }
+
+ public ValidationMessages addError(String key, String label) {
+ errors.add(new Message(key, label));
+ return this;
+ }
+
+ public ValidationMessages addWarning(String key, String label) {
+ warnings.add(new Message(key, label));
+ return this;
+ }
+
+ public ValidationMessages addInfo(String key, String label) {
+ infos.add(new Message(key, label));
+ return this;
+ }
+
+ public static final class Message {
+ private String key;
+ private String label;
+
+ private Message(String key, String label) {
+ this.key = key;
+ this.label = label;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/WildcardPattern.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/WildcardPattern.java
new file mode 100644
index 00000000000..6d93a40a06f
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/WildcardPattern.java
@@ -0,0 +1,107 @@
+/*
+ * 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.utils;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * @since 1.10
+ */
+public class WildcardPattern {
+
+ private static final Map<String, WildcardPattern> patterns = new HashMap<String, WildcardPattern>();
+
+ private Pattern pattern;
+
+ protected WildcardPattern(String pattern, String directorySeparator) {
+ this.pattern = Pattern.compile(toRegexp(pattern, directorySeparator));
+ }
+
+ public boolean match(String value) {
+ return pattern.matcher(removeSlahesToIgnore(value)).matches();
+ }
+
+ private String toRegexp(String wildcardPattern, String directorySeparator) {
+ String patternStr = removeSlahesToIgnore(wildcardPattern);
+ patternStr = StringUtils.replace(patternStr, "**/**", "**");
+ patternStr = StringUtils.replace(patternStr, "**/", "|");
+ patternStr = StringUtils.replace(patternStr, "/**", "|");
+ patternStr = StringUtils.replace(patternStr, "**", "|");
+ StringBuilder sb = new StringBuilder();
+
+ for (char c : patternStr.toCharArray()) {
+ switch (c) {
+ case '|':
+ sb.append(".*");
+ break;
+ case '*':
+ sb.append("[^\\").append(directorySeparator).append("]*");
+ break;
+ case '?':
+ sb.append("[^\\").append(directorySeparator).append("]");
+ break;
+ case '.':
+ sb.append("\\.");
+ break;
+ case '/':
+ sb.append("\\").append(directorySeparator);
+ break;
+ default:
+ sb.append(c);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ private String removeSlahesToIgnore(String wildcardPattern) {
+ String patternStr = StringUtils.removeStart(wildcardPattern, "/");
+ return StringUtils.removeEnd(patternStr, "/");
+ }
+
+ public static WildcardPattern create(String pattern) {
+ return create(pattern, "/");
+ }
+
+ public static WildcardPattern[] create(String[] patterns) {
+ if (patterns==null) {
+ return new WildcardPattern[0];
+ }
+ WildcardPattern[] exclusionPAtterns = new WildcardPattern[patterns.length];
+ for (int i = 0; i < patterns.length; i++) {
+ exclusionPAtterns[i] = create(patterns[i]);
+ }
+ return exclusionPAtterns;
+ }
+
+ public static WildcardPattern create(String pattern, String directorySeparator) {
+ String key = pattern + directorySeparator;
+ WildcardPattern wildcardPattern = patterns.get(key);
+ if (wildcardPattern == null) {
+ wildcardPattern = new WildcardPattern(pattern, directorySeparator);
+ patterns.put(key, wildcardPattern);
+ }
+ return wildcardPattern;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/XmlParserException.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/XmlParserException.java
new file mode 100644
index 00000000000..e81b23b28af
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/XmlParserException.java
@@ -0,0 +1,40 @@
+/*
+ * 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.utils;
+
+/**
+ * @since 1.10
+ */
+public class XmlParserException extends SonarException {
+ public XmlParserException() {
+ }
+
+ public XmlParserException(String s) {
+ super(s);
+ }
+
+ public XmlParserException(String s, Throwable throwable) {
+ super(s, throwable);
+ }
+
+ public XmlParserException(Throwable throwable) {
+ super(throwable);
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/XpathParser.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/XpathParser.java
new file mode 100644
index 00000000000..6dff6e01fc9
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/XpathParser.java
@@ -0,0 +1,268 @@
+/*
+ * 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.utils;
+
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.xpath.*;
+
+/**
+ * XML Parsing tool using XPATH. It's recommended to use StaxParser when parsing big XML files.
+ *
+ * @since 1.10
+ */
+public class XpathParser {
+
+ private Element root = null;
+ private Document doc = null;
+ private DocumentBuilder builder;
+ private XPath xpath;
+ private Map<String, XPathExpression> compiledExprs = new HashMap<String, XPathExpression>();
+
+ public XpathParser() {
+ DocumentBuilderFactory bf = DocumentBuilderFactory.newInstance();
+ try {
+ bf.setFeature("http://apache.org/xml/features/validation/schema", false);
+ bf.setFeature("http://xml.org/sax/features/external-general-entities", false);
+ bf.setFeature("http://xml.org/sax/features/validation", false);
+ bf.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false);
+ bf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
+ bf.setFeature("http://apache.org/xml/features/allow-java-encodings", true);
+ } catch (ParserConfigurationException e) {
+ Logger log = LoggerFactory.getLogger(this.getClass().getName());
+ log.error("Error occured during features set up.", e);
+ }
+ try {
+ bf.setNamespaceAware(false);
+ bf.setValidating(false);
+ builder = bf.newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ throw new XmlParserException("can not create a XML parser", e);
+ }
+ }
+
+ public void parse(File file) {
+ if (file == null || !file.exists()) {
+ throw new XmlParserException("File not found : " + file);
+ }
+
+ BufferedReader buffer = null;
+ try {
+ buffer = new BufferedReader(new FileReader(file));
+ parse(buffer);
+
+ } catch (IOException e) {
+ throw new XmlParserException("can not parse the file " + file.getAbsolutePath(), e);
+
+ } finally {
+ IOUtils.closeQuietly(buffer);
+ }
+ }
+
+ public void parse(InputStream stream) {
+ BufferedReader buffer = null;
+ try {
+ buffer = new BufferedReader(new InputStreamReader(stream));
+ parse(buffer);
+
+ } catch (IOException e) {
+ throw new XmlParserException("can not parse the stream", e);
+
+ } finally {
+ IOUtils.closeQuietly(buffer);
+ }
+ }
+
+ private void parse(BufferedReader buffer) throws IOException {
+ parse(IOUtils.toString(buffer));
+ }
+
+ public void parse(String xml) {
+ try {
+ xml = fixUnicodeChar(xml);
+ doc = builder.parse(new ByteArrayInputStream(xml.getBytes()));
+ XPathFactory factory = XPathFactory.newInstance();
+ xpath = factory.newXPath();
+
+ } catch (SAXException e) {
+ throw new XmlParserException("can not parse xml : " + xml, e);
+ } catch (IOException e) {
+ throw new XmlParserException("can not parse xml : " + xml, e);
+ }
+ }
+
+ public Element getRoot() {
+ if (root == null && doc != null) {
+ root = doc.getDocumentElement();
+ }
+ return root;
+ }
+
+ public Document getDocument() {
+ return doc;
+ }
+
+ public Element getChildElement(Element base, String elementName) {
+ NodeList childrens = base.getElementsByTagName(elementName);
+ for (int i = 0; i < childrens.getLength(); i++) {
+ Node nde = childrens.item(i);
+ if (nde.getNodeType() == Node.ELEMENT_NODE) {
+ return (Element) nde;
+ }
+ }
+ return null;
+ }
+
+ public Element getChildElement(String elementName) {
+ NodeList childrens = getRoot().getElementsByTagName(elementName);
+ for (int i = 0; i < childrens.getLength(); i++) {
+ Node nde = childrens.item(i);
+ if (nde.getNodeType() == Node.ELEMENT_NODE) {
+ return (Element) nde;
+ }
+ }
+ return null;
+ }
+
+ public List<Element> getChildElements(String elementName) {
+ List<Element> rtrVal = new ArrayList<Element>();
+ NodeList childrens = getRoot().getElementsByTagName(elementName);
+ for (int i = 0; i < childrens.getLength(); i++) {
+ Node nde = childrens.item(i);
+ if (nde.getNodeType() == Node.ELEMENT_NODE) {
+ rtrVal.add((Element) nde);
+ }
+ }
+ return rtrVal;
+ }
+
+ public List<Element> getChildElements(Element base, String elementName) {
+ List<Element> rtrVal = new ArrayList<Element>();
+ NodeList childrens = base.getElementsByTagName(elementName);
+ for (int i = 0; i < childrens.getLength(); i++) {
+ Node nde = childrens.item(i);
+ if (nde.getNodeType() == Node.ELEMENT_NODE) {
+ rtrVal.add((Element) nde);
+ }
+ }
+ return rtrVal;
+ }
+
+ public String getChildElementValue(Element base, String elementName) {
+ NodeList childrens = base.getElementsByTagName(elementName);
+ for (int i = 0; i < childrens.getLength(); i++) {
+ if (childrens.item(i).getNodeType() == Node.ELEMENT_NODE) {
+ return childrens.item(i).getFirstChild().getNodeValue();
+ }
+ }
+ return null;
+ }
+
+ public String getElementValue(Node base) {
+ if (base.getNextSibling() != null && base.getNextSibling().getNodeType() == Node.TEXT_NODE) {
+ return base.getNextSibling().getNodeValue();
+ } else if (base.getFirstChild() != null && base.getFirstChild().getNodeType() == Node.TEXT_NODE) {
+ return base.getFirstChild().getNodeValue();
+ }
+ return null;
+ }
+
+ public String getChildElementValue(String elementName) {
+ NodeList childrens = getRoot().getElementsByTagName(elementName);
+ for (int i = 0; i < childrens.getLength(); i++) {
+ if (childrens.item(i).getNodeType() == Node.ELEMENT_NODE) {
+ return childrens.item(i).getFirstChild().getNodeValue();
+ }
+ }
+ return null;
+ }
+
+ public Object executeXPath(Node node, QName qname, String xPathExpression) {
+ XPathExpression expr = compiledExprs.get(xPathExpression);
+ try {
+ if (expr == null) {
+ expr = xpath.compile(xPathExpression);
+ compiledExprs.put(xPathExpression, expr);
+ }
+ return expr.evaluate(node, qname);
+
+ } catch (XPathExpressionException e) {
+ throw new XmlParserException("Unable to evaluate xpath expression :" + xPathExpression, e);
+ }
+ }
+
+ public String executeXPath(String xPathExpression) {
+ return (String) executeXPath(doc, XPathConstants.STRING, xPathExpression);
+ }
+
+ public String executeXPath(Node node, String xPathExpression) {
+ return (String) executeXPath(node, XPathConstants.STRING, xPathExpression);
+ }
+
+ public NodeList executeXPathNodeList(String xPathExpression) {
+ return (NodeList) executeXPath(doc, XPathConstants.NODESET, xPathExpression);
+ }
+
+ public NodeList executeXPathNodeList(Node node, String xPathExpression) {
+ return (NodeList) executeXPath(node, XPathConstants.NODESET, xPathExpression);
+ }
+
+ public Node executeXPathNode(Node node, String xPathExpression) {
+ return (Node) executeXPath(node, XPathConstants.NODE, xPathExpression);
+ }
+
+ /**
+ * Fix the error occured when parsing a string containing unicode character
+ * Example : &u20ac; will be replaced by &#x20ac;
+ */
+ protected String fixUnicodeChar(String text) {
+ String unicode = "&u";
+ StringBuilder replace = new StringBuilder(text);
+ if (text.indexOf(unicode) >= 0) {
+ Pattern p = Pattern.compile("&u([0-9a-fA-F]{1,4});");
+ Matcher m = p.matcher(replace.toString());
+ int nbFind = 0;
+ while (m.find()) {
+ // Add one index each time because we add one character each time (&u -> &#x)
+ replace.replace(m.start() + nbFind, m.end() + nbFind, "&#x" + m.group(1) + ";");
+ nbFind++;
+ }
+ }
+ return replace.toString();
+ }
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/ZipUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/ZipUtils.java
new file mode 100644
index 00000000000..fdefda6ac62
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/ZipUtils.java
@@ -0,0 +1,165 @@
+/*
+ * 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.utils;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+
+import java.io.*;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * @since 1.10
+ */
+public final class ZipUtils {
+
+ private ZipUtils() {
+ // only static methods
+ }
+
+ /**
+ * Unzip a file into a new temporary directory. The directory is not deleted on JVM exit, so it
+ * must be explicitely deleted.
+ * @return the temporary directory
+ * @since 2.2
+ */
+ public static File unzipToTempDir(File zip) throws IOException {
+ File toDir = TempFileUtils.createTempDirectory();
+ unzip (zip, toDir);
+ return toDir;
+ }
+
+ /**
+ * Unzip a file into a directory. The directory is created if it does not exist.
+ * @return the target directory
+ */
+ public static File unzip(File zip, File toDir) throws IOException {
+ unzip(zip, toDir, new ZipEntryFilter() {
+ public boolean accept(ZipEntry entry) {
+ return true;
+ }
+ });
+ return toDir;
+ }
+
+ public static void unzip(File zip, File toDir, ZipEntryFilter filter) throws IOException {
+ if (!toDir.exists()) {
+ FileUtils.forceMkdir(toDir);
+ }
+
+ ZipFile zipFile = new ZipFile(zip);
+ try {
+ Enumeration<? extends ZipEntry> entries = zipFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = entries.nextElement();
+ if (filter.accept(entry)) {
+ File to = new File(toDir, entry.getName());
+ if (entry.isDirectory()) {
+ if (!to.exists() && !to.mkdirs()) {
+ throw new IOException("Error creating directory: " + to);
+ }
+ } else {
+ File parent = to.getParentFile();
+ if (parent != null && !parent.exists() && !parent.mkdirs()) {
+ throw new IOException("Error creating directory: " + parent);
+ }
+
+ FileOutputStream fos = new FileOutputStream(to);
+ try {
+ IOUtils.copy(zipFile.getInputStream(entry), fos);
+ } finally {
+ IOUtils.closeQuietly(fos);
+ }
+ }
+ }
+ }
+ } finally {
+ zipFile.close();
+ }
+ }
+
+ public static void zipDir(File dir, File zip) throws IOException {
+ OutputStream out = null;
+ ZipOutputStream zout = null;
+ try {
+ out = FileUtils.openOutputStream(zip);
+ zout = new ZipOutputStream(out);
+ zip(dir, zout, null);
+
+ } finally {
+ IOUtils.closeQuietly(zout);
+ IOUtils.closeQuietly(out);
+ }
+ }
+
+
+ private static void _zip(String entryName, InputStream in, ZipOutputStream out) throws IOException {
+ ZipEntry zentry = new ZipEntry(entryName);
+ out.putNextEntry(zentry);
+ IOUtils.copy(in, out);
+ out.closeEntry();
+ }
+
+ private static void _zip(String entryName, File file, ZipOutputStream out) throws IOException {
+ if (file.isDirectory()) {
+ entryName += '/';
+ ZipEntry zentry = new ZipEntry(entryName);
+ out.putNextEntry(zentry);
+ out.closeEntry();
+ File[] files = file.listFiles();
+ for (int i = 0, len = files.length; i < len; i++) {
+ _zip(entryName + files[i].getName(), files[i], out);
+ }
+
+ } else {
+ InputStream in = null;
+ try {
+ in = new BufferedInputStream(new FileInputStream(file));
+ _zip(entryName, in, out);
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
+ }
+
+ private static void zip(File file, ZipOutputStream out, String prefix) throws IOException {
+ if (prefix != null) {
+ int len = prefix.length();
+ if (len == 0) {
+ prefix = null;
+ } else if (prefix.charAt(len - 1) != '/') {
+ prefix += '/';
+ }
+ }
+ for (File child : file.listFiles()) {
+ String name = prefix != null ? prefix + child.getName() : child.getName();
+ _zip(name, child, out);
+ }
+ }
+
+ public static interface ZipEntryFilter {
+ boolean accept(ZipEntry entry);
+ }
+
+}
+
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/dag/CyclicDependenciesException.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/dag/CyclicDependenciesException.java
new file mode 100644
index 00000000000..b35f037da94
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/dag/CyclicDependenciesException.java
@@ -0,0 +1,43 @@
+/*
+ * 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.utils.dag;
+
+import org.sonar.api.utils.SonarException;
+
+/**
+ * @since 1.10
+ */
+public class CyclicDependenciesException extends SonarException {
+ public CyclicDependenciesException() {
+ }
+
+ public CyclicDependenciesException(String s) {
+ super(s);
+ }
+
+ public CyclicDependenciesException(String s, Throwable throwable) {
+ super(s, throwable);
+ }
+
+ public CyclicDependenciesException(Throwable throwable) {
+ super(throwable);
+ }
+}
+
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/dag/DirectAcyclicGraph.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/dag/DirectAcyclicGraph.java
new file mode 100644
index 00000000000..f6348e87ee0
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/dag/DirectAcyclicGraph.java
@@ -0,0 +1,93 @@
+/*
+ * 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.utils.dag;
+
+import org.sonar.api.utils.SonarException;
+
+import java.util.*;
+
+/**
+ * <a href="http://en.wikipedia.org/wiki/Directed_acyclic_graph">http://en.wikipedia.org/wiki/Directed_acyclic_graph</a>
+ *
+ * @since 1.10
+ */
+public class DirectAcyclicGraph {
+
+ private Map<Object, Node> registeredObjects = new HashMap<Object, Node>();
+ private List<Node> nodes = new ArrayList<Node>();
+
+ public DirectAcyclicGraph(final Object... objects) {
+ if (objects != null) {
+ for (Object object : objects) {
+ add(object);
+ }
+ }
+ }
+
+ public Node add(final Object object, final Object... dependencies) {
+ Node node = registeredObjects.get(object);
+ if (node == null) {
+ node = new Node(object);
+ nodes.add(node);
+ registeredObjects.put(object, node);
+ }
+
+ if (dependencies != null) {
+ for (Object dependency : dependencies) {
+ Node depNode = add(dependency);
+ node.addDependency(depNode);
+ }
+ }
+ return node;
+ }
+
+ public List sort() {
+ sortNodes();
+
+ List result = new ArrayList();
+ for (Node node : nodes) {
+ result.add(node.getObject());
+ }
+ return result;
+ }
+
+ private List<Node> sortNodes() {
+ verify();
+ Collections.sort(nodes);
+ return nodes;
+ }
+
+ private void verify() {
+ for (Node node : nodes) {
+ node.reset();
+ }
+
+ for (Node node : nodes) {
+ for (Node dep : node.getDependencies()) {
+ if (!nodes.contains(dep)) {
+ throw new SonarException("A dependent node (" + dep + ") of "
+ + " (" + node + ") was not included in the nodes list.");
+ }
+ }
+
+ node.resolveOrder();
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/utils/dag/Node.java b/sonar-plugin-api/src/main/java/org/sonar/api/utils/dag/Node.java
new file mode 100644
index 00000000000..b47cda548d3
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/utils/dag/Node.java
@@ -0,0 +1,126 @@
+/*
+ * 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.utils.dag;
+
+import org.sonar.api.utils.SonarException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @since 1.10
+ */
+public class Node implements Comparable<Node> {
+
+ private final Object object;
+ private int order = 0;
+ private boolean seen = false;
+ private final List<Node> dependencies = new ArrayList<Node>();
+
+ public Node(final Object object) {
+ if (object == null) {
+ throw new SonarException("object can not be null");
+ }
+ this.object = object;
+ }
+
+ public void reset() {
+ order = 0;
+ seen = false;
+ }
+
+ public Object getObject() {
+ return object;
+ }
+
+ public void addDependency(Node v) {
+ if (!equals(v) && !dependencies.contains(v)) {
+ dependencies.add(v);
+ }
+ }
+
+ public void resolveOrder() throws CyclicDependenciesException {
+ resolveOrder(toString());
+ }
+
+ private int resolveOrder(String path) throws CyclicDependenciesException {
+ seen = true;
+ try {
+ int highOrder = -1;
+ for (Node dep : dependencies) {
+ if (dep.seen) {
+ throw new CyclicDependenciesException(path + " -> " + dep.toString());
+ }
+ highOrder = Math.max(highOrder, dep.resolveOrder(path + " -> " + dep.toString()));
+
+ }
+
+ order = highOrder + 1;
+ return order;
+
+ } finally {
+ seen = false;
+ }
+ }
+
+ public List<Node> getDependencies() {
+ return dependencies;
+ }
+
+ public int compareTo(final Node other) {
+ int orderInd = 0;
+
+ if (order < other.order) {
+ orderInd = -1;
+ } else if (order > other.order) {
+ orderInd = 1;
+ }
+
+ return orderInd;
+ }
+
+ public int getOrder() {
+ return order;
+ }
+
+ @Override
+ public String toString() {
+ return object.toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof Node)) {
+ return false;
+ }
+
+ return object.equals(((Node) o).getObject());
+ }
+
+ @Override
+ public int hashCode() {
+ return object.hashCode();
+ }
+
+}
+
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/AbstractDashboardWidget.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/AbstractDashboardWidget.java
new file mode 100644
index 00000000000..be9d0415405
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/AbstractDashboardWidget.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.web;
+
+/**
+ * @since 1.10
+ * @deprecated override org.sonar.api.web.AbstractRubyTemplate and implement org.sonar.api.web.RubyRailsWidget
+ */
+@Deprecated
+public abstract class AbstractDashboardWidget extends AbstractRubyTemplate implements RubyRailsWidget {
+
+ public String getId() {
+ return getClass().toString();
+ }
+
+ public String getTitle() {
+ return getClass().toString();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/AbstractRubyTemplate.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/AbstractRubyTemplate.java
new file mode 100644
index 00000000000..a9f9c22428c
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/AbstractRubyTemplate.java
@@ -0,0 +1,100 @@
+/*
+ * 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.web;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.sonar.api.utils.SonarException;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * It's useful in development environment to see browser rendering in real time while editing the template. To do that, just
+ * return an absolute path in the method getTemplatePath() :<br/>
+ * <pre>
+ * <code>
+ * protected String getTemplatePath() {
+ * return "/tmp/sample_dashboard_widget.erb";
+ * }
+ * </code>
+ * </pre>
+ * Build and deploy the plugin in /extensions/plugins. The file /tmp/sample_dashboard_widget.erb will be reloaded on each request.
+ * <p/>
+ * <br/>
+ * In production environment, you have to return the classloader path, for example "/org/sonar/myplugin/sample_dashboard_widget.erb".
+ *
+ * @since 1.11
+ */
+public abstract class AbstractRubyTemplate {
+
+ private String cache = null;
+
+ public String getTemplate() {
+ String result = loadTemplateFromCache();
+ try {
+ if (result == null) {
+ result = loadTemplateFromClasspath();
+ }
+ if (result == null) {
+ result = loadTemplateFromAbsolutePath();
+ }
+ return result;
+
+ } catch (IOException e) {
+ throw new SonarException("Can not read the file " + getTemplatePath(), e);
+ }
+ }
+
+ private String loadTemplateFromAbsolutePath() throws IOException {
+ File file = new File(getTemplatePath());
+ if (file.exists()) {
+ // the result is not cached
+ return FileUtils.readFileToString(file);
+ }
+ throw new FileNotFoundException(getTemplatePath());
+ }
+
+ private String loadTemplateFromClasspath() throws IOException {
+ InputStream input = getClass().getResourceAsStream(getTemplatePath());
+ try {
+ if (input != null) {
+ cache = IOUtils.toString(input);
+ return cache;
+ }
+ } finally {
+ IOUtils.closeQuietly(input);
+ }
+ return null;
+ }
+
+ protected String loadTemplateFromCache() {
+ return cache;
+ }
+
+ /**
+ * the path of the template. In production environment, it's the classloader path (for example "/org/sonar/my_template.erb").
+ * In dev mode, it's useful to return an absolute path (for example C:/temp/my_template.erb). In such a case the result is not cached.
+ */
+ protected abstract String getTemplatePath();
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/CodeColorizerFormat.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/CodeColorizerFormat.java
new file mode 100644
index 00000000000..21d2894aeea
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/CodeColorizerFormat.java
@@ -0,0 +1,73 @@
+/*
+ * 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.web;
+
+import org.sonar.api.ServerExtension;
+import org.sonar.channel.Channel;
+import org.sonar.colorizer.HtmlCodeBuilder;
+import org.sonar.colorizer.Tokenizer;
+
+import java.util.List;
+
+/**
+ * Extend the library sonar-colorizer to support new languages. By default only Java sources are colorized in Sonar.
+ *
+ * @since 1.12
+ */
+public abstract class CodeColorizerFormat implements ServerExtension {
+
+ private String languageKey;
+
+ /**
+ * @param languageKey the unique sonar key. Not null.
+ */
+ protected CodeColorizerFormat(String languageKey) {
+ this.languageKey = languageKey;
+ }
+
+ public final String getLanguageKey() {
+ return languageKey;
+ }
+
+ /**
+ * sonar-colorizer tokenizers for HTML output.
+ * @return a not null list (empty if no tokenizers)
+ */
+ public abstract List<Tokenizer> getTokenizers();
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof CodeColorizerFormat)) {
+ return false;
+ }
+
+ CodeColorizerFormat format = (CodeColorizerFormat) o;
+ return languageKey.equals(format.languageKey);
+
+ }
+
+ @Override
+ public int hashCode() {
+ return languageKey.hashCode();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/DefaultTab.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/DefaultTab.java
new file mode 100644
index 00000000000..27bb8cc9bfe
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/DefaultTab.java
@@ -0,0 +1,37 @@
+/*
+ * 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.web;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @since 2.0
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface DefaultTab {
+
+ // default value is all metrics
+ String[] metrics() default {};
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/Footer.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/Footer.java
new file mode 100644
index 00000000000..0bf9f56ee21
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/Footer.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.web;
+
+import org.sonar.api.ServerExtension;
+
+/**
+ * @since 1.10
+ */
+public interface Footer extends ServerExtension {
+
+ /**
+ * Static HTML (no Ruby on Rails nor GWT)
+ */
+ String getHtml();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/GwtExtension.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/GwtExtension.java
new file mode 100644
index 00000000000..1281aa966c7
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/GwtExtension.java
@@ -0,0 +1,29 @@
+/*
+ * 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.web;
+
+import org.sonar.api.ServerExtension;
+
+/**
+ * @since 1.10
+ */
+public interface GwtExtension extends ServerExtension {
+ String getGwtId();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/GwtPage.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/GwtPage.java
new file mode 100644
index 00000000000..b4942b2a6c8
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/GwtPage.java
@@ -0,0 +1,31 @@
+/*
+ * 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.web;
+
+/**
+ * @since 1.11
+ */
+public abstract class GwtPage implements Page, GwtExtension {
+
+ public final String getId() {
+ return getGwtId();
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/NavigationSection.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/NavigationSection.java
new file mode 100644
index 00000000000..7a669c5a048
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/NavigationSection.java
@@ -0,0 +1,45 @@
+/*
+ * 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.web;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The Page is displayed only in listed sections. This annotation is ignored on Widgets.
+ *
+ * @since 1.11
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface NavigationSection {
+
+ public static final String HOME = "home";
+ public static final String RESOURCE = "resource";
+ public static final String RESOURCE_TAB = "resource_tab";
+ public static final String CONFIGURATION = "configuration";
+
+
+ String[] value() default {HOME};
+
+}
+
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/Page.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/Page.java
new file mode 100644
index 00000000000..ebed61f4f0a
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/Page.java
@@ -0,0 +1,27 @@
+/*
+ * 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.web;
+
+/**
+ * @since 2.0
+ */
+public interface Page extends View {
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/ResourceLanguage.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/ResourceLanguage.java
new file mode 100644
index 00000000000..7c8a80d3af8
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/ResourceLanguage.java
@@ -0,0 +1,42 @@
+/*
+ * 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.web;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The view is displayed only if the selected resource has the same language.
+ *
+ * @since 1.11
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface ResourceLanguage {
+
+ /**
+ * An empty value means all resource scopes.
+ */
+ String[] value() default {};
+
+}
+
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/ResourceQualifier.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/ResourceQualifier.java
new file mode 100644
index 00000000000..7b6a02cfa0a
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/ResourceQualifier.java
@@ -0,0 +1,42 @@
+/*
+ * 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.web;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The view is displayed only if the selected resource has the same qualifier.
+ *
+ * @since 1.11
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface ResourceQualifier {
+
+ /**
+ * An empty value means all resource scopes.
+ */
+ String[] value() default {};
+
+}
+
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/ResourceScope.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/ResourceScope.java
new file mode 100644
index 00000000000..82c1e7ea4db
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/ResourceScope.java
@@ -0,0 +1,42 @@
+/*
+ * 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.web;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * The view is displayed only if the selected resource has the same qualifier.
+ *
+ * @since 1.11
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface ResourceScope {
+
+ /**
+ * An empty value means all resource scopes.
+ */
+ String[] value() default {};
+
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/RubyRailsPage.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/RubyRailsPage.java
new file mode 100644
index 00000000000..7f802040597
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/RubyRailsPage.java
@@ -0,0 +1,32 @@
+/*
+ * 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.web;
+
+/**
+ * @since 1.11
+ */
+public interface RubyRailsPage extends Page {
+
+ /**
+ * @return Content of the Ruby on Rails template
+ */
+ String getTemplate();
+
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/RubyRailsWebservice.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/RubyRailsWebservice.java
new file mode 100644
index 00000000000..5746873914e
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/RubyRailsWebservice.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.web;
+
+
+/**
+ * <b>EXPERIMENTAL</b>
+ * <p/>
+ * Interface to create a ruby web service extension point using the Ruby On Rails controller API (ActionController)
+ * The method getTemplate() return the ROR controller code, the name of the controller class defined in the template
+ * MUST match the following name scheme : Api::$Webservice.getId()Controller I.E : Webservice.getId() = TestWS > Api::TestWSController.
+ * The plugin will be deployed with the following URL scheme: http://sonarhost/api/plugins/$Webservice.getId()/:action/:id
+ * :action is the name of the controller method to call, :id is a param that will be passed to the controller, these 2 params are not mandatory
+ * and will call the index controler method if not specified.
+ *
+ * @since 1.11
+ */
+public interface RubyRailsWebservice extends Webservice {
+
+ /**
+ * @return Content of the Ruby on Rails web service controller class
+ */
+ String getTemplate();
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/RubyRailsWidget.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/RubyRailsWidget.java
new file mode 100644
index 00000000000..be2d0418919
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/RubyRailsWidget.java
@@ -0,0 +1,40 @@
+/*
+ * 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.web;
+
+/**
+ * Widget in project dashboard page. It's recommended to also extend AbstractRubyTemplate :
+ * <p/>
+ * <code>
+ * public class MyWidget extends AbstractRubyTemplate implements RubyRailsWidget {
+ * protected String getTemplatePath() {
+ * return "/myplugin/my_template.erb";
+ * }
+ * }
+ * </code>
+ *
+ * @since 1.11
+ */
+public interface RubyRailsWidget extends Widget {
+ /**
+ * @return content of the Ruby on Rails template
+ */
+ String getTemplate();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/Section.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/Section.java
new file mode 100644
index 00000000000..8f118df0660
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/Section.java
@@ -0,0 +1,29 @@
+/*
+ * 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.web;
+
+/**
+ * @since 1.10
+ * @deprecated add annotation org.sonar.api.web.NavigationSection to View extensions
+ */
+@Deprecated
+public enum Section {
+ HOME, PROJECT, CONFIGURATION
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/UserRole.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/UserRole.java
new file mode 100644
index 00000000000..227497903e5
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/UserRole.java
@@ -0,0 +1,45 @@
+/*
+ * 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.web;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @since 1.11
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface UserRole {
+
+ /**
+ * @deprecated use the constant USER since 1.12.
+ */
+ @Deprecated public static final String VIEWER = "user";
+
+ public static final String USER = "user";
+ public static final String ADMIN = "admin";
+ public static final String CODEVIEWER = "codeviewer";
+
+ String[] value() default {};
+
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/View.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/View.java
new file mode 100644
index 00000000000..3e3dfabc9d4
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/View.java
@@ -0,0 +1,32 @@
+/*
+ * 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.web;
+
+import org.sonar.api.ServerExtension;
+
+/**
+ * @since 1.11
+ */
+public interface View extends ServerExtension {
+
+ String getId();
+
+ String getTitle();
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/Webservice.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/Webservice.java
new file mode 100644
index 00000000000..9610b0183c8
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/Webservice.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.web;
+
+import org.sonar.api.ServerExtension;
+
+/**
+ * Interface to create a web service extension point
+ *
+ * @since 1.11
+ */
+public interface Webservice extends ServerExtension {
+
+ /**
+ * @return The id of the web service
+ */
+ String getId();
+
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/Widget.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/Widget.java
new file mode 100644
index 00000000000..016dc481391
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/Widget.java
@@ -0,0 +1,26 @@
+/*
+ * 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.web;
+
+/**
+ * @since 1.11
+ */
+public interface Widget extends View {
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/AbstractPage.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/AbstractPage.java
new file mode 100644
index 00000000000..9e0842af352
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/AbstractPage.java
@@ -0,0 +1,50 @@
+/*
+ * 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.web.gwt.client;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * @deprecated since 2.0, use the lib sonar-gwt-api
+ */
+@Deprecated
+public abstract class AbstractPage implements EntryPoint {
+
+ protected void displayView(Widget widget) {
+ Element loading = DOM.getElementById("loading");
+ if (loading != null) {
+ DOM.removeChild(getRootPanel().getElement(), loading);
+ }
+ getRootPanel().add(widget);
+ }
+
+ protected RootPanel getRootPanel() {
+ RootPanel rootPanel = RootPanel.get("gwtpage-" + GWT.getModuleName());
+ if (rootPanel == null) {
+ rootPanel = RootPanel.get("gwtpage");
+ }
+ return rootPanel;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/AbstractViewer.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/AbstractViewer.java
new file mode 100644
index 00000000000..0d442fd541a
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/AbstractViewer.java
@@ -0,0 +1,216 @@
+/*
+ * 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.web.gwt.client;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.Widget;
+import org.sonar.api.web.gwt.client.webservices.*;
+
+import java.util.Arrays;
+
+public abstract class AbstractViewer implements EntryPoint {
+
+ public static final String HTML_ROOT_ID = "resource_viewers";
+
+ private Resource resource;
+ private String renderedResourceKey = "";
+ private Panel widgetPanel = null;
+ private boolean standAloneMode = true;
+
+ public void onModuleLoad() {
+ exportJavascript();
+ }
+
+ /**
+ * Export GWT javascript methods to load and control the plugin, must export currently 2 method :
+ * I.E for plugin GWT id : foo.bar.MyPlugin, class foo.bar.client.MyPlugin :
+ * <p/>
+ * $wnd.load_foo_bar_MyPlugin = function() {
+ * called to the plugin init from JS
+ * obj.@foo.bar.client.MyPlugin::loadContainer()();
+ * }
+ * $wnd.on_resource_loaded_foo_bar_MyPlugin = function() {
+ * called when a resource JSON object has been refreshed within the page
+ * obj.@foo.bar.client.MyPlugin::onResourceLoaded()();
+ * }
+ */
+ protected abstract void exportJavascript();
+
+ /**
+ * When multiple widgets are bound to the same HTML div, this method will indicate
+ * If the resource widget is the default one to show when the widget is initialized
+ *
+ * @param metric the metric for which the widget is shown, cannot be null
+ * @param resource the resource bound to the widget
+ * @return true or false
+ */
+ protected abstract boolean isDefault(WSMetrics.Metric metric, Resource resource);
+
+ /**
+ * Finds if a given metric is in the provided metrics list
+ *
+ * @param metric the metric to search
+ * @param metricsList the metric list
+ * @return true or false if not found
+ */
+ protected boolean isMetricInList(WSMetrics.Metric metric, WSMetrics.Metric... metricsList) {
+ return Arrays.asList(metricsList).contains(metric);
+ }
+
+ /**
+ * When multiple widgets are in the same page, this method will indicate if the widget
+ * can be shown for the given resource
+ *
+ * @param resource the resource bound to the page
+ * @return true or false
+ */
+ protected abstract boolean isForResource(Resource resource);
+
+
+ public Resource getResource() {
+ return resource;
+ }
+
+ private Resource loadResource() {
+ JavaScriptObject resourceJson = getResourceJSONObject();
+ if (resourceJson != null) {
+ Resource resourceLoaded = ResourcesQuery.parseResources(resourceJson).get(0);
+ String currentMetricKey = ResourceDictionary.getViewerMetricKey();
+ Boolean isDefaultForMetric = false;
+ if (currentMetricKey != null) {
+ isDefaultForMetric = isDefault(WSMetrics.get(currentMetricKey), resourceLoaded);
+ }
+ exportJSBooleanVariable("is_default_for_metric", Utils.widgetGWTIdJSEncode(getGwtId()), isDefaultForMetric);
+ exportJSBooleanVariable("is_for_resource", Utils.widgetGWTIdJSEncode(getGwtId()), isForResource(resourceLoaded));
+ return resourceLoaded;
+ }
+ return null;
+ }
+
+ /**
+ * Called when a resource JSON object has been loaded within the page
+ */
+ public final void onResourceLoaded() {
+ resource = loadResource();
+ standAloneMode = false;
+ }
+
+ /**
+ * Called to render the widget for the given resource object loaded via the onResourceLoaded() method call
+ */
+ public final void loadContainer() {
+ String resourceKey = ResourceDictionary.getViewerResourceKey();
+ if (resourceKey != null) {
+ if (!standAloneMode && resource == null) {
+ Utils.showError("Unable to find JSON resource object, unable to render widget");
+ return;
+ } else if (standAloneMode && resource == null) {
+ getResourceJsonObject(resourceKey);
+ return;
+ }
+ String currentResourceKey = isANumber(resourceKey) ? resource.getId().toString() : resource.getKey();
+ if (!renderedResourceKey.equals(currentResourceKey)) {
+ // resource key has changed reload if not in standalone mode
+ if (!standAloneMode) {
+ resource = loadResource();
+ }
+
+ if (widgetPanel == null) {
+ RootPanel rootPanel = RootPanel.get(HTML_ROOT_ID);
+ if (rootPanel == null) {
+ Utils.showError("Unable to find root panel " + HTML_ROOT_ID + " in page");
+ }
+ widgetPanel = new FlowPanel();
+ widgetPanel.setStyleName("gwt-ResourceTab");
+ String panelId = "tab-" + Utils.widgetGWTIdJSEncode(getGwtId());
+ widgetPanel.getElement().setId(panelId);
+ registerTab(panelId);
+ widgetPanel.setVisible(false);
+ rootPanel.add(widgetPanel);
+ }
+
+ renderedResourceKey = resourceKey;
+
+ if (widgetPanel != null) {
+ widgetPanel.clear();
+ widgetPanel.add(render(resource));
+ }
+ }
+ }
+
+ if (widgetPanel != null) {
+ widgetPanel.setVisible(true);
+ }
+ }
+
+ private static native void registerTab(Object tabId) /*-{
+ $wnd.registeredTabs.push(tabId);
+ }-*/;
+
+
+ private native void exportJSBooleanVariable(String varPrefix, String encodedGWTId, boolean value)/*-{
+ $wnd.config[varPrefix + "_" + encodedGWTId] = value;
+ }-*/;
+
+ /**
+ * Return the GWT id of the widget
+ */
+ protected abstract String getGwtId();
+
+ /**
+ * Renders the widget for the current resource
+ */
+ protected abstract Widget render(Resource resource);
+
+ /**
+ * Return a JavaScriptObject object containing all the measure available for the current resource key
+ *
+ * @return the JavaScriptObject instance, should never be null
+ */
+ protected native JavaScriptObject getResourceJSONObject()/*-{
+ return $wnd.config['current_resource'];
+ }-*/;
+
+
+ private boolean isANumber(String resourceKey) {
+ boolean isIdResourceKey = true;
+ try {
+ Integer.parseInt(resourceKey);
+ } catch (NumberFormatException ex) {
+ isIdResourceKey = false;
+ }
+ return isIdResourceKey;
+ }
+
+ private void getResourceJsonObject(String resourceKey) {
+ ResourcesQuery.get(resourceKey).execute(new StandAloneResourceHandler());
+ }
+
+ public class StandAloneResourceHandler extends BaseQueryCallback<Resources> {
+ public void onResponse(Resources resources, JavaScriptObject jsonResponse) {
+ resource = resources.firstResource();
+ loadContainer();
+ }
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/ResourceDictionary.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/ResourceDictionary.java
new file mode 100644
index 00000000000..7e45a702174
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/ResourceDictionary.java
@@ -0,0 +1,59 @@
+/*
+ * 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.web.gwt.client;
+
+import com.google.gwt.i18n.client.Dictionary;
+
+public final class ResourceDictionary {
+
+ public final static String CONF_PERMALINK_BASE = "permalink_url_base";
+ public final static String CONF_RESOURCE_KEY = "resource_key";
+ public final static String CONF_V_RESOURCE_KEY = "viewer_resource_key";
+ public final static String CONF_V_PLUGIN_KEY = "viewer_plugin_key";
+ public final static String CONF_V_METRIC_KEY = "metric";
+
+ private ResourceDictionary() {
+ }
+
+ public static String getPermaLinkURLBase() {
+ return Utils.getConfiguration(CONF_PERMALINK_BASE);
+ }
+
+ public static String getResourceKey() {
+ return Utils.getConfiguration(CONF_RESOURCE_KEY);
+ }
+
+ public static String getViewerResourceKey() {
+ return Utils.getConfiguration(CONF_V_RESOURCE_KEY);
+ }
+
+ public static String getViewerPluginKey() {
+ return Utils.getConfiguration(CONF_V_PLUGIN_KEY);
+ }
+
+ public static String getViewerMetricKey() {
+ return Utils.getConfiguration(CONF_V_METRIC_KEY);
+ }
+
+ public static Dictionary getResourceViewers() {
+ return Dictionary.getDictionary("resource_viewers");
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/Utils.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/Utils.java
new file mode 100644
index 00000000000..7bcee0fe809
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/Utils.java
@@ -0,0 +1,154 @@
+/*
+ * 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.web.gwt.client;
+
+import com.google.gwt.i18n.client.Dictionary;
+import com.google.gwt.i18n.client.NumberFormat;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.Window;
+import org.sonar.api.web.gwt.client.webservices.Resource;
+
+import java.util.Set;
+
+/**
+ * A class of web utility
+ *
+ * @since 1.10
+ */
+public final class Utils {
+ private Utils() {
+ }
+
+ public static String getConfiguration(String key) {
+ return getConfiguration(key, null);
+ }
+
+ public static String getConfiguration(String key, String defaultValue) {
+ String result = getDictionaryEntry("config", key);
+ if (result == null) {
+ result = defaultValue;
+ }
+ return result;
+ }
+
+ public static native void setConfiguration(String key, String val) /*-{
+ $wnd.config[key] = val;
+ }-*/;
+
+ public static String getRequestParameter(String key) {
+ return getDictionaryEntry("request_parameters", key);
+ }
+
+ public static Set<String> getConfigurationKeys() {
+ return getDictionaryKeys("config");
+ }
+
+ public static Set<String> getRequestParameterNames() {
+ return getDictionaryKeys("request_parameters");
+ }
+
+ private static String getDictionaryEntry(String dictionaryName, String key) {
+ try {
+ Dictionary dic = Dictionary.getDictionary(dictionaryName);
+ if (dic != null) {
+ return dic.get(key);
+ }
+ return null;
+
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ private static Set<String> getDictionaryKeys(String dictionaryName) {
+ Dictionary dic = Dictionary.getDictionary(dictionaryName);
+ if (dic != null) {
+ return dic.keySet();
+ }
+ return null;
+ }
+
+ public static String widgetGWTIdJSEncode(String widgetGWTId) {
+ return widgetGWTId.replace('.', '_');
+ }
+
+ public static String getServerUrl() {
+ return getConfiguration("sonar_url");
+ }
+
+ public static String getServerApiUrl() {
+ return getServerUrl() + "/api";
+ }
+
+ public static String escapeHtml(String maybeHtml) {
+ final Element div = DOM.createDiv();
+ DOM.setInnerText(div, maybeHtml);
+ return DOM.getInnerHTML(div);
+ }
+
+ public static String formatPercent(String percentage) {
+ return percentage == null || percentage.equals("") ? "" : formatPercent(new Double(percentage));
+ }
+
+ public static String formatPercent(double percentage) {
+ return NumberFormat.getFormat("0.0").format(percentage) + "%";
+ }
+
+ public static String formatNumber(String number) {
+ return number == null || number.equals("") ? "" : formatNumber(new Double(number));
+ }
+
+ public static String formatNumber(double number) {
+ return NumberFormat.getDecimalFormat().format(number);
+ }
+
+ public static native void showError(String message) /*-{
+ $wnd.error(message);
+ }-*/;
+
+ public static native void showWarning(String message) /*-{
+ $wnd.warning(message);
+ }-*/;
+
+ public static native void showInfo(String message) /*-{
+ $wnd.info(message);
+ }-*/;
+
+ /**
+ * Display the resource in a popup.
+ *
+ * @param resource the resource to display, not null
+ * @param metricKey the metric to highlight (optional : can be null)
+ */
+ public static void openResourcePopup(final Resource resource, final String metricKey) {
+ String url = Utils.getServerUrl() + "/resource/index/" + resource.getId();
+ if (metricKey != null) {
+ url += "?" + ResourceDictionary.CONF_V_METRIC_KEY + "=" + metricKey;
+ }
+ Window.open(url, "resource", "height=800,width=900,scrollbars=1,resizable=1");
+ }
+
+ public static String getUrlToRuleDescription(final String ruleKey, final boolean showLayout) {
+ return Utils.getServerUrl() + "/rules/show/" + ruleKey + "?layout=" + showLayout;
+ }
+}
+
+
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/AbstractResourceQuery.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/AbstractResourceQuery.java
new file mode 100644
index 00000000000..9c2e6c2de8a
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/AbstractResourceQuery.java
@@ -0,0 +1,40 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+
+public abstract class AbstractResourceQuery<R extends ResponsePOJO> extends Query<R> {
+
+ private String resourceKey;
+
+ protected AbstractResourceQuery(String resourceKey) {
+ super();
+ this.resourceKey = resourceKey;
+ }
+
+ public String getResourceKey() {
+ return resourceKey;
+ }
+
+ public void setResourceKey(String resourceKey) {
+ this.resourceKey = resourceKey;
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/BaseQueryCallback.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/BaseQueryCallback.java
new file mode 100644
index 00000000000..119422740a5
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/BaseQueryCallback.java
@@ -0,0 +1,52 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+import org.sonar.api.web.gwt.client.Utils;
+import org.sonar.api.web.gwt.client.widgets.LoadingLabel;
+
+public abstract class BaseQueryCallback<P extends ResponsePOJO> implements QueryCallBack<P> {
+
+ private LoadingLabel loading;
+
+ public BaseQueryCallback() {
+ this(null);
+ }
+
+ public BaseQueryCallback(LoadingLabel loading) {
+ super();
+ this.loading = loading;
+ }
+
+ public void onError(int errorCode, String errorMessage) {
+ Utils.showError("Error received from server : " + errorCode + " - " + errorMessage);
+ if (loading != null) {
+ loading.removeFromParent();
+ }
+ }
+
+ public void onTimeout() {
+ Utils.showWarning("JSON query response timeout");
+ if (loading != null) {
+ loading.removeFromParent();
+ }
+ }
+
+} \ No newline at end of file
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/FileSource.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/FileSource.java
new file mode 100644
index 00000000000..82224296a97
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/FileSource.java
@@ -0,0 +1,37 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+import java.util.Map;
+
+public class FileSource extends ResponsePOJO {
+
+ private Map<Integer, String> sourceLines;
+
+ public FileSource(Map<Integer, String> sourceLines) {
+ super();
+ this.sourceLines = sourceLines;
+ }
+
+ public Map<Integer, String> getLines() {
+ return sourceLines;
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/JSONHandlerDispatcher.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/JSONHandlerDispatcher.java
new file mode 100644
index 00000000000..ee70c4993bd
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/JSONHandlerDispatcher.java
@@ -0,0 +1,48 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+public abstract class JSONHandlerDispatcher<P extends ResponsePOJO> implements JsonUtils.JSONHandler {
+
+ private QueryCallBack<P> callBack;
+
+ public JSONHandlerDispatcher(QueryCallBack<P> callBack) {
+ super();
+ this.callBack = callBack;
+ }
+
+ public abstract P parseResponse(JavaScriptObject obj);
+
+ public void onError(int errorCode, String errorMessage) {
+ callBack.onError(errorCode, errorMessage);
+ }
+
+ public void onResponse(JavaScriptObject obj) {
+ P responseObj = parseResponse(obj);
+ callBack.onResponse(responseObj, obj);
+ }
+
+ public void onTimeout() {
+ callBack.onTimeout();
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/JsonUtils.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/JsonUtils.java
new file mode 100644
index 00000000000..42a11919474
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/JsonUtils.java
@@ -0,0 +1,182 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+import java.util.Date;
+
+import com.google.gwt.core.client.JavaScriptException;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.http.client.URL;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.json.client.JSONArray;
+import com.google.gwt.json.client.JSONBoolean;
+import com.google.gwt.json.client.JSONNumber;
+import com.google.gwt.json.client.JSONObject;
+import com.google.gwt.json.client.JSONString;
+import com.google.gwt.json.client.JSONValue;
+
+public final class JsonUtils {
+ private static int requestId = 0;
+
+ private JsonUtils() {
+
+ }
+
+ public interface JSONHandler {
+ void onResponse(JavaScriptObject obj);
+
+ void onTimeout();
+
+ void onError(int errorCode, String errorMessage);
+ }
+
+ public static void requestJson(String url, JSONHandler handler) {
+ if (!url.endsWith("&") && !url.endsWith("?")) {
+ url += "&";
+ }
+ if (!url.contains("format=json")) {
+ url += "format=json&";
+ }
+ if (!url.contains("callback=")) {
+ //IMPORTANT : the url should ended with ?callback= or &callback= for JSONP calls
+ url += "callback=";
+ }
+ makeJSONRequest(requestId++, URL.encode(url), handler);
+ }
+
+ public static native void makeJSONRequest(int requestId, String url, JSONHandler handler) /*-{
+ var callback = "callback" + requestId;
+
+ // create SCRIPT tag, and set SRC attribute equal to JSON feed URL + callback function name
+ var script = document.createElement("script");
+ script.setAttribute("src", url+callback);
+ script.setAttribute("type", "text/javascript");
+
+ window[callback] = function(jsonObj) {
+ @org.sonar.api.web.gwt.client.webservices.JsonUtils::dispatchJSON(Lcom/google/gwt/core/client/JavaScriptObject;Lorg/sonar/api/web/gwt/client/webservices/JsonUtils$JSONHandler;)(jsonObj, handler);
+ window[callback + "done"] = true;
+ }
+
+ setTimeout(function() {
+ if (!window[callback + "done"]) {
+ handler.@org.sonar.api.web.gwt.client.webservices.JsonUtils.JSONHandler::onTimeout();
+ }
+
+ // cleanup
+ document.body.removeChild(script);
+ if (window[callback]) {
+ delete window[callback];
+ }
+ if (window[callback + "done"]) {
+ delete window[callback + "done"];
+ }
+ }, 120000);
+
+ document.body.appendChild(script);
+ }-*/;
+
+ public static void dispatchJSON(JavaScriptObject jsonObj, JSONHandler handler) {
+ JSONObject obj = new JSONObject(jsonObj);
+ if (obj.isObject() != null) {
+ if (obj.containsKey("err_code")) {
+ handler.onError(new Double(obj.get("err_code").isNumber().doubleValue()).intValue(),
+ obj.get("err_msg").isString().stringValue());
+ return;
+ }
+ }
+ handler.onResponse(jsonObj);
+ }
+
+ public static String getString(JSONObject json, String field) {
+ JSONValue jsonValue;
+ JSONString jsonString;
+ if ((jsonValue = json.get(field)) == null) {
+ return null;
+ }
+ if ((jsonString = jsonValue.isString()) == null) {
+ JSONNumber jsonNumber = jsonValue.isNumber();
+ return jsonNumber != null ? jsonNumber.toString() : null;
+ }
+ return jsonString.stringValue();
+ }
+
+ public static Date getDate(JSONObject json, String field) {
+ DateTimeFormat frmt = DateTimeFormat.getFormat("yyyy-MM-dd'T'HH:mm:ssZ");
+ String date = getString(json, field);
+ if (date!=null && date.endsWith("Z") && date.length()>2) {
+ // see SONAR-1182
+ date = date.substring(0, date.length()-2) + "+00:00";
+ }
+ return frmt.parse(date);
+ }
+
+ public static Boolean getBoolean(JSONObject json, String field) {
+ JSONValue jsonValue;
+ JSONBoolean jsonBoolean;
+ if ((jsonValue = json.get(field)) == null) {
+ return null;
+ }
+ if ((jsonBoolean = jsonValue.isBoolean()) == null) {
+ return null;
+ }
+ return jsonBoolean.booleanValue();
+ }
+
+ public static Double getDouble(JSONObject json, String field) {
+ JSONValue jsonValue;
+ JSONNumber jsonNumber;
+ if ((jsonValue = json.get(field)) == null) {
+ return null;
+ }
+ if ((jsonNumber = jsonValue.isNumber()) == null) {
+ return null;
+ }
+ return jsonNumber.doubleValue();
+ }
+
+ public static Integer getInteger(JSONObject json, String field) {
+ final Double d = getDouble(json, field);
+ if (d != null) {
+ return d.intValue();
+ }
+ return null;
+ }
+
+ public static JSONObject getArray(JSONValue json, int i) {
+ if (json instanceof JSONArray) {
+ return ((JSONArray) json).get(i).isObject();
+ }
+ if (json instanceof JSONObject) {
+ return ((JSONObject) json).get(Integer.toString(i)).isObject();
+ }
+ throw new JavaScriptException("Not implemented");
+ }
+
+ public static int getArraySize(JSONValue array) {
+ if (array instanceof JSONArray) {
+ return ((JSONArray) array).size();
+ }
+ if (array instanceof JSONObject) {
+ return ((JSONObject) array).size();
+ }
+ throw new JavaScriptException("Not implemented");
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Measure.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Measure.java
new file mode 100644
index 00000000000..a9cc3c5aee5
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Measure.java
@@ -0,0 +1,152 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class Measure {
+ private String metric;
+ private String metricName;
+ private Double value;
+ private String formattedValue;
+ private String data;
+
+ private String ruleKey;
+ private String ruleName;
+ private String ruleCategory;
+ private String rulePriority;
+
+ private Date date;
+
+ public Measure() {
+ }
+
+ public Measure(String metric, Double value, String formattedValue) {
+ this.metric = metric;
+ this.value = value;
+ this.formattedValue = formattedValue;
+ }
+
+ public String getMetric() {
+ return metric;
+ }
+
+ public void setMetric(String metric) {
+ this.metric = metric;
+ }
+
+ public Double getValue() {
+ return value;
+ }
+
+ public void setValue(Double value) {
+ this.value = value;
+ }
+
+ public String getFormattedValue() {
+ return formattedValue;
+ }
+
+ public void setFormattedValue(String formattedValue) {
+ this.formattedValue = formattedValue;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public Map<String, String> getDataAsMap() {
+ Map<String, String> map = new TreeMap<String, String>();
+ if (data != null) {
+ String[] strings = data.split(";");
+ for (String string : strings) {
+ String[] keyValue = string.split("=");
+ map.put(keyValue[0], keyValue[1]);
+ }
+ }
+ return map;
+
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+
+ public String getMetricName() {
+ return metricName;
+ }
+
+ public void setMetricName(String metricName) {
+ this.metricName = metricName;
+ }
+
+ public String getRuleKey() {
+ return ruleKey;
+ }
+
+ public void setRuleKey(String s) {
+ this.ruleKey = s;
+ }
+
+ public String getRuleName() {
+ return ruleName;
+ }
+
+ public void setRuleName(String ruleName) {
+ this.ruleName = ruleName;
+ }
+
+ public String getRuleCategory() {
+ return ruleCategory;
+ }
+
+ public void setRuleCategory(String s) {
+ this.ruleCategory = s;
+ }
+
+ public String getRulePriority() {
+ return rulePriority;
+ }
+
+ public void setRulePriority(String rulePriority) {
+ this.rulePriority = rulePriority;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ @Override
+ public String toString() {
+ return "Measure{" +
+ "metric='" + metric + '\'' +
+ ", metric_name='" + metricName + '\'' +
+ ", val='" + value + '\'' +
+ ", f_val='" + formattedValue + '\'' +
+ '}';
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/MetricsQuery.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/MetricsQuery.java
new file mode 100644
index 00000000000..b63268842a8
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/MetricsQuery.java
@@ -0,0 +1,104 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.sonar.api.web.gwt.client.Utils;
+import org.sonar.api.web.gwt.client.webservices.WSMetrics.MetricsList;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.json.client.JSONArray;
+import com.google.gwt.json.client.JSONObject;
+
+public final class MetricsQuery extends Query<MetricsList> {
+
+ private Boolean userManaged;
+ private List<WSMetrics.Metric.ValueType> excludedTypes = new ArrayList<WSMetrics.Metric.ValueType>();
+
+ public static MetricsQuery get() {
+ return new MetricsQuery();
+ }
+
+ private MetricsQuery() {
+ super();
+ }
+
+ public Boolean isUserManaged() {
+ return userManaged;
+ }
+
+ public MetricsQuery setUserManaged(Boolean userManaged) {
+ this.userManaged = userManaged;
+ return this;
+ }
+
+ public MetricsQuery excludeTypes(WSMetrics.Metric.ValueType... types) {
+ for (WSMetrics.Metric.ValueType valueType : types) {
+ excludedTypes.add(valueType);
+ }
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return Utils.getServerApiUrl() + "/metrics?";
+ }
+
+ @Override
+ public void execute(QueryCallBack<MetricsList> callback) {
+ JsonUtils.requestJson(this.toString(), new JSONHandlerDispatcher<MetricsList>(callback) {
+ @Override
+ public MetricsList parseResponse(JavaScriptObject obj) {
+ return parseMetrics(obj);
+ }
+ });
+ }
+
+ private MetricsList parseMetrics(JavaScriptObject json) {
+ JSONArray array = new JSONArray(json);
+ MetricsList list = new MetricsList();
+ for (int i = 0; i < array.size(); i++) {
+ JSONObject jsStock = array.get(i).isObject();
+ if (jsStock != null) {
+ WSMetrics.Metric m = parseMetric(jsStock);
+ boolean skip = (isUserManaged() != null && (!isUserManaged() && m.isUserManaged())) || excludedTypes.contains(m.getType());
+ if (!skip) {
+ list.getMetrics().add(m);
+ }
+ }
+ }
+ return list;
+ }
+
+ private WSMetrics.Metric parseMetric(JSONObject json) {
+ String key = JsonUtils.getString(json, "key");
+ String name = JsonUtils.getString(json, "name");
+ String description = JsonUtils.getString(json, "description");
+ String domain = JsonUtils.getString(json, "domain");
+ String type = JsonUtils.getString(json, "val_type");
+ boolean qualitative = JsonUtils.getBoolean(json, "qualitative");
+ boolean userManaged = JsonUtils.getBoolean(json, "user_managed");
+ Integer direction = JsonUtils.getInteger(json, "direction");
+ return new WSMetrics.Metric(key, name, description, domain, qualitative, userManaged, direction, WSMetrics.Metric.ValueType.valueOf(type));
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Properties.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Properties.java
new file mode 100644
index 00000000000..a31ea97ff62
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Properties.java
@@ -0,0 +1,44 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+import java.util.List;
+
+public class Properties extends ResponsePOJO {
+
+ private List<Property> properties;
+
+ public Properties(List<Property> properties) {
+ this.properties = properties;
+ }
+
+ public List<Property> getProperties() {
+ return properties;
+ }
+
+ public String get(String key, String defaultValue) {
+ for (Property property : properties) {
+ if( property.getKey().equals(key)) {
+ return property.getValue();
+ }
+ }
+ return defaultValue;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/PropertiesQuery.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/PropertiesQuery.java
new file mode 100644
index 00000000000..0b8b5df53dc
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/PropertiesQuery.java
@@ -0,0 +1,75 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.json.client.JSONArray;
+import com.google.gwt.json.client.JSONObject;
+import org.sonar.api.web.gwt.client.Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class PropertiesQuery extends Query<Properties> {
+
+ private String key;
+
+ public PropertiesQuery() {
+ }
+
+ public PropertiesQuery(String key) {
+ this.key = key;
+ }
+
+ @Override
+ public String toString() {
+ String url = Utils.getServerApiUrl() + "/properties";
+ if (key != null) {
+ url += "/" + key;
+ }
+ return url + "?";
+ }
+
+ @Override
+ public void execute(QueryCallBack<Properties> callback) {
+ JsonUtils.requestJson(this.toString(), new JSONHandlerDispatcher<Properties>(callback) {
+ @Override
+ public Properties parseResponse(JavaScriptObject obj) {
+ return new Properties(parseProperties(obj));
+ }
+
+ private List<Property> parseProperties(JavaScriptObject obj) {
+ JSONArray array = new JSONArray(obj);
+ List<Property> properties = new ArrayList<Property>();
+ for (int i = 0; i < array.size(); i++) {
+ JSONObject jsonObject = array.get(i).isObject();
+ if (jsonObject != null) {
+ properties.add(parseProperty(jsonObject));
+ }
+ }
+ return properties;
+ }
+
+ private Property parseProperty(JSONObject json) {
+ return new Property(JsonUtils.getString(json, "key"), JsonUtils.getString(json, "value"));
+ }
+ });
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Property.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Property.java
new file mode 100644
index 00000000000..21167df4688
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Property.java
@@ -0,0 +1,52 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+public class Property extends ResponsePOJO {
+
+ private String key;
+ private String value;
+
+ public Property() {
+ }
+
+ public Property(String key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public Property setKey(String key) {
+ this.key = key;
+ return this;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public Property setValue(String value) {
+ this.value = value;
+ return this;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Query.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Query.java
new file mode 100644
index 00000000000..ea573ab0bb3
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Query.java
@@ -0,0 +1,25 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+public abstract class Query<R extends ResponsePOJO> {
+
+ public abstract void execute(final QueryCallBack<R> callback);
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/QueryCallBack.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/QueryCallBack.java
new file mode 100644
index 00000000000..431e07ebb9b
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/QueryCallBack.java
@@ -0,0 +1,32 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+public interface QueryCallBack<RESPONSE_POJO extends ResponsePOJO> {
+
+ void onResponse(RESPONSE_POJO response, JavaScriptObject jsonRawResponse);
+
+ void onTimeout();
+
+ void onError(int errorCode, String errorMessage);
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Resource.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Resource.java
new file mode 100644
index 00000000000..6389fc6aa9c
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Resource.java
@@ -0,0 +1,187 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+import java.util.List;
+
+public class Resource extends ResponsePOJO {
+ public static final String SCOPE_SET = "PRJ";
+ public static final String SCOPE_SPACE = "DIR";
+ public static final String SCOPE_ENTITY = "FIL";
+
+ @Deprecated
+ public static final String SCOPE_PROJECT = SCOPE_SET;
+ @Deprecated
+ public static final String SCOPE_DIRECTORY = SCOPE_SPACE;
+ @Deprecated
+ public static final String SCOPE_FILE = SCOPE_ENTITY;
+
+ public static final String QUALIFIER_PROJECT = "TRK";
+ public static final String QUALIFIER_MODULE = "BRC";
+ @Deprecated
+ public static final String QUALIFIER_PROJECT_TRUNK = QUALIFIER_PROJECT;
+ @Deprecated
+ public static final String QUALIFIER_PROJECT_BRANCH = QUALIFIER_MODULE;
+ public static final String QUALIFIER_PACKAGE = "PAC";
+ public static final String QUALIFIER_DIRECTORY = "DIR";
+ public static final String QUALIFIER_FILE = "FIL";
+ public static final String QUALIFIER_CLASS = "CLA";
+ public static final String QUALIFIER_UNIT_TEST = "UTS";
+
+ private Integer id;
+ private String key;
+ private String name;
+ private String longName;
+ private String qualifier;
+ private String scope;
+ private String language;
+ private Integer copy;
+ private List<Measure> measures;
+
+ public Resource() {
+ }
+
+ public Resource(Integer id, String key, String name, String scope, String qualifier, String language, Integer copy, List<Measure> measures) {
+ this.id = id;
+ this.key = key;
+ this.name = name;
+ this.qualifier = qualifier;
+ this.scope = scope;
+ this.language = language;
+ this.measures = measures;
+ this.copy = copy;
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getName(boolean longFormatIfDefined) {
+ if (longFormatIfDefined && longName != null && !"".equals(longName)) {
+ return longName;
+ }
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getLongName() {
+ return longName;
+ }
+
+ public void setLongName(String longName) {
+ this.longName = longName;
+ }
+
+ public String getQualifier() {
+ return qualifier;
+ }
+
+ public void setQualifier(String qualifier) {
+ this.qualifier = qualifier;
+ }
+
+ public String getScope() {
+ return scope;
+ }
+
+ public void setScope(String scope) {
+ this.scope = scope;
+ }
+
+ public String getLanguage() {
+ return language;
+ }
+
+ public void setLanguage(String language) {
+ this.language = language;
+ }
+
+ public Integer getCopy() {
+ return copy;
+ }
+
+ public void setCopy(Integer copy) {
+ this.copy = copy;
+ }
+
+ public List<Measure> getMeasures() {
+ return measures;
+ }
+
+ public Measure getMeasure(WSMetrics.Metric metric) {
+ if (measures != null) {
+ for (Measure measure : measures) {
+ if (measure.getMetric().equals(metric.getKey())) {
+ return measure;
+ }
+ }
+ }
+ return null;
+ }
+
+ public boolean hasMeasure(WSMetrics.Metric metric) {
+ return getMeasure(metric) != null;
+ }
+
+ public String getMeasureFormattedValue(WSMetrics.Metric metric, String defaultValue) {
+ Measure measure = getMeasure(metric);
+ if (measure != null) {
+ return measure.getFormattedValue();
+ }
+ return defaultValue;
+ }
+
+ public void setMeasures(List<Measure> measures) {
+ this.measures = measures;
+ }
+
+ public boolean matchesKey(String resourceKey) {
+ return resourceKey != null && (getId().toString().equals(resourceKey) || getKey().equals(resourceKey));
+ }
+
+ @Override
+ public String toString() {
+ return "Resource{" +
+ "id='" + id + '\'' +
+ ", key='" + key + '\'' +
+ ", name='" + name + '\'' +
+ ", longName='" + longName + '\'' +
+ ", scope='" + scope + '\'' +
+ ", qualifier='" + qualifier + '\'' +
+ ", language='" + language + '\'' +
+ ", measures=" + measures +
+ '}';
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Resources.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Resources.java
new file mode 100644
index 00000000000..b3a91971a88
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Resources.java
@@ -0,0 +1,63 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+import java.util.List;
+
+public class Resources extends ResponsePOJO {
+
+ private List<Resource> resources;
+
+ public Resources(List<Resource> resources) {
+ super();
+ this.resources = resources;
+ }
+
+ public List<Resource> getResources() {
+ return resources;
+ }
+
+ public Resource firstResource() {
+ return resources.size() > 0 ? resources.get(0) : null;
+ }
+
+ public boolean onceContainsMeasure(WSMetrics.Metric metric) {
+ for (Resource resource : resources) {
+ if (resource.getMeasure(metric) != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean allContainsMeasure(WSMetrics.Metric metric) {
+ for (Resource resource : resources) {
+ if (resource.getMeasure(metric) == null) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isEmpty() {
+ return resources == null || resources.isEmpty();
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/ResourcesQuery.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/ResourcesQuery.java
new file mode 100644
index 00000000000..fe6125e486a
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/ResourcesQuery.java
@@ -0,0 +1,256 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.json.client.JSONArray;
+import com.google.gwt.json.client.JSONObject;
+import com.google.gwt.json.client.JSONValue;
+import org.sonar.api.web.gwt.client.Utils;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public final class ResourcesQuery extends AbstractResourceQuery<Resources> {
+
+ public final static int DEPTH_UNLIMITED = -1;
+
+ private Integer depth;
+ private Integer limit;
+ private String scopes;
+ private String qualifiers;
+ private String metrics;
+ private String rules;
+ private String ruleCategories;
+ private String rulePriorities;
+ private boolean verbose = false;
+
+ /**
+ * Alias for build()
+ */
+ public static ResourcesQuery get(String resourceKey) {
+ return new ResourcesQuery(resourceKey);
+ }
+
+ public static ResourcesQuery build(String resourceKey) {
+ return new ResourcesQuery(resourceKey);
+ }
+
+ public static ResourcesQuery build() {
+ return new ResourcesQuery(null);
+ }
+
+ private ResourcesQuery(String resourceKey) {
+ super(resourceKey);
+ }
+
+ public ResourcesQuery setDepth(Integer depth) {
+ this.depth = depth;
+ return this;
+ }
+
+ public ResourcesQuery setRules(String s) {
+ this.rules = s;
+ return this;
+ }
+
+ public ResourcesQuery filterOnRules(boolean b) {
+ return setRules(b ? "true" : "false");
+ }
+
+ public ResourcesQuery filterOnRulePriorities(boolean b) {
+ return setRulePriorities(b ? "true" : "false");
+ }
+
+ public ResourcesQuery filterOnRuleCategories(boolean b) {
+ return setRuleCategories(b ? "true" : "false");
+ }
+
+ public ResourcesQuery setRulePriorities(String s) {
+ this.rulePriorities = s;
+ return this;
+ }
+
+ public ResourcesQuery setRuleCategories(String s) {
+ this.ruleCategories = s;
+ return this;
+ }
+
+ public ResourcesQuery setLimit(Integer limit) {
+ this.limit = limit;
+ return this;
+ }
+
+ public ResourcesQuery setScopes(String scopes) {
+ this.scopes = scopes;
+ return this;
+ }
+
+ public ResourcesQuery setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ return this;
+ }
+
+ public ResourcesQuery setQualifiers(String qualifiers) {
+ this.qualifiers = qualifiers;
+ return this;
+ }
+
+ public ResourcesQuery setMetrics(List<WSMetrics.Metric> metrics) {
+ this.metrics = getMetricsWSRequest(metrics);
+ return this;
+ }
+
+ public ResourcesQuery setMetric(WSMetrics.Metric m) {
+ this.metrics = m.getKey();
+ return this;
+ }
+
+ public ResourcesQuery setMetric(String metricKey) {
+ this.metrics = metricKey;
+ return this;
+ }
+
+ private String getMetricsWSRequest(List<WSMetrics.Metric> metrics) {
+ StringBuilder metricsDelimByComma = new StringBuilder(64);
+ for (WSMetrics.Metric metric : metrics) {
+ metricsDelimByComma.append(metric.getKey()).append(",");
+ }
+ return metricsDelimByComma.substring(0, metricsDelimByComma.length() - 1);
+ }
+
+ @Override
+ public String toString() {
+ String url = Utils.getServerApiUrl() + "/resources?";
+ if (getResourceKey() != null) {
+ url += "resource=" + getResourceKey() + "&";
+ }
+ if (metrics != null) {
+ url += "metrics=" + metrics + "&";
+ }
+ if (scopes != null) {
+ url += "scopes=" + scopes + "&";
+ }
+ if (qualifiers != null) {
+ url += "qualifiers=" + qualifiers + "&";
+ }
+ if (depth != null) {
+ url += "depth=" + depth + "&";
+ }
+ if (limit != null) {
+ url += "limit=" + limit + "&";
+ }
+ if (rules != null) {
+ url += "rules=" + rules + "&";
+ }
+ if (ruleCategories != null) {
+ url += "rule_categories=" + ruleCategories + "&";
+ }
+ if (rulePriorities != null) {
+ url += "rule_priorities=" + rulePriorities + "&";
+ }
+ if (verbose) {
+ url += "verbose=true&";
+ }
+ return url;
+ }
+
+ @Override
+ public void execute(QueryCallBack<Resources> callback) {
+ JsonUtils.requestJson(this.toString(), new JSONHandlerDispatcher<Resources>(callback) {
+ @Override
+ public Resources parseResponse(JavaScriptObject obj) {
+ return new Resources(parseResources(obj));
+ }
+ });
+ }
+
+ public static List<Resource> parseResources(JavaScriptObject json) {
+ JSONArray array = new JSONArray(json);
+ List<Resource> resources = new ArrayList<Resource>();
+ for (int i = 0; i < array.size(); i++) {
+ JSONObject jsStock = array.get(i).isObject();
+ if (jsStock != null) {
+ resources.add(parseResource(jsStock));
+ }
+ }
+ return resources;
+ }
+
+ private static Resource parseResource(JSONObject json) {
+ Double id = JsonUtils.getDouble(json, "id");
+ String key = JsonUtils.getString(json, "key");
+ String name = JsonUtils.getString(json, "name");
+ String longName = JsonUtils.getString(json, "lname");
+ String qualifier = JsonUtils.getString(json, "qualifier");
+ String language = JsonUtils.getString(json, "lang");
+ String scope = JsonUtils.getString(json, "scope");
+ Integer copy = JsonUtils.getInteger(json, "copy");
+ Date date = JsonUtils.getDate(json, "date");
+
+ List<Measure> measures = null;
+ JSONValue measuresJson;
+ if ((measuresJson = json.get("msr")) != null) {
+ measures = parseMeasures(measuresJson, date);
+ }
+
+ final Resource resource = new Resource(id.intValue(), key, name, scope, qualifier, language, copy, measures);
+ resource.setLongName(longName);
+ return resource;
+ }
+
+ private static List<Measure> parseMeasures(JSONValue measures, Date date) {
+ List<Measure> projectMeasures = new ArrayList<Measure>();
+ int len = JsonUtils.getArraySize(measures);
+ for (int i = 0; i < len; i++) {
+ JSONObject measure = JsonUtils.getArray(measures, i);
+ if (measure != null) {
+ Measure measureEntry = parseMeasure(measure, date);
+ if (measureEntry != null) {
+ projectMeasures.add(measureEntry);
+ }
+ }
+ }
+ return projectMeasures;
+ }
+
+ private static Measure parseMeasure(JSONObject measure, Date date) {
+ String metric = JsonUtils.getString(measure, "key");
+ if (metric == null) {
+ return null;
+ }
+
+ final Measure m = new Measure(metric, JsonUtils.getDouble(measure, "val"), JsonUtils.getString(measure, "frmt_val"));
+ m.setData(JsonUtils.getString(measure, "data"));
+ String metricName = JsonUtils.getString(measure, "name");
+ if (metricName != null) {
+ m.setMetricName(metricName);
+ }
+
+ m.setRuleKey(JsonUtils.getString(measure, "rule_key"));
+ m.setRuleName(JsonUtils.getString(measure, "rule_name"));
+ m.setRuleCategory(JsonUtils.getString(measure, "rule_category"));
+ m.setRulePriority(JsonUtils.getString(measure, "rule_priority"));
+ m.setDate(date);
+ return m;
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/ResponsePOJO.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/ResponsePOJO.java
new file mode 100644
index 00000000000..7accea548bf
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/ResponsePOJO.java
@@ -0,0 +1,27 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+/**
+ * Marker class for WS query response objects
+ */
+public abstract class ResponsePOJO {
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Rule.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Rule.java
new file mode 100644
index 00000000000..c150e48c63b
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Rule.java
@@ -0,0 +1,57 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+public class Rule extends ResponsePOJO {
+ private String key;
+ private String name;
+ private String category;
+
+ public Rule(String key, String name, String category) {
+ this.key = key;
+ this.name = name;
+ this.category = category;
+ }
+
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public void setCategory(String category) {
+ this.category = category;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/SequentialQueries.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/SequentialQueries.java
new file mode 100644
index 00000000000..dd827891912
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/SequentialQueries.java
@@ -0,0 +1,112 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.user.client.Timer;
+
+public class SequentialQueries extends Query<VoidResponse> {
+
+ private List<AjaxQuery<?>> queries = new ArrayList<AjaxQuery<?>>();
+ private int sleepTimeBetweenCbChecks;
+
+ private SequentialQueries(int sleepTimeBetweenCbChecks) {
+ this.sleepTimeBetweenCbChecks = sleepTimeBetweenCbChecks;
+ }
+
+ public static SequentialQueries get() {
+ return new SequentialQueries(50);
+ }
+
+ public static SequentialQueries get(int sleepTimeBetweenCbChecks) {
+ return new SequentialQueries(sleepTimeBetweenCbChecks);
+ }
+
+ public <R extends ResponsePOJO> SequentialQueries add(Query<R> query, QueryCallBack<R> callback) {
+ queries.add(new AjaxQuery<R>(query, callback));
+ return this;
+ }
+
+ @Override
+ public void execute(final QueryCallBack<VoidResponse> callback) {
+ for (AjaxQuery<?> query : queries) {
+ query.execute();
+ }
+ Timer queriesMonitor = new Timer() {
+ @Override
+ public void run() {
+ boolean queriesExecuted = true;
+ for (AjaxQuery<?> query : queries) {
+ if (!query.isCompleted()) {
+ queriesExecuted = false;
+ break;
+ }
+ }
+ if (queriesExecuted) {
+ callback.onResponse(new VoidResponse(), null);
+ cancel();
+ }
+ }
+ };
+ queriesMonitor.scheduleRepeating(sleepTimeBetweenCbChecks);
+ }
+
+ private class AjaxQuery<R extends ResponsePOJO> {
+ private Query<R> query;
+ private QueryCallBack<R> callback;
+
+ private boolean completed = false;
+
+ public AjaxQuery(Query<R> query, QueryCallBack<R> callback) {
+ super();
+ this.query = query;
+ this.callback = callback;
+ }
+
+ private void execute() {
+ QueryCallBack<R> proxy = new QueryCallBack<R>() {
+ public void onError(int errorCode, String errorMessage) {
+ callback.onError(errorCode, errorMessage);
+ completed = true;
+ }
+
+ public void onResponse(R response, JavaScriptObject jsonRawResponse) {
+ callback.onResponse(response, jsonRawResponse);
+ completed = true;
+ }
+
+ public void onTimeout() {
+ callback.onTimeout();
+ completed = true;
+ }
+ };
+ query.execute(proxy);
+ }
+
+ public boolean isCompleted() {
+ return completed;
+ }
+
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/SourcesQuery.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/SourcesQuery.java
new file mode 100644
index 00000000000..1d1b0498df2
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/SourcesQuery.java
@@ -0,0 +1,101 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.json.client.JSONArray;
+import com.google.gwt.json.client.JSONObject;
+import com.google.gwt.json.client.JSONValue;
+import org.sonar.api.web.gwt.client.Utils;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+public final class SourcesQuery extends AbstractResourceQuery<FileSource> {
+
+ private Integer from;
+ private Integer length;
+ private boolean color;
+
+ public static SourcesQuery get(String resourceKey) {
+ return new SourcesQuery(resourceKey);
+ }
+
+ private SourcesQuery(String resourceKey) {
+ super(resourceKey);
+ }
+
+ public SourcesQuery setFrom(Integer from) {
+ this.from = from;
+ return this;
+ }
+
+ public SourcesQuery setLength(Integer length) {
+ this.length = length;
+ return this;
+ }
+
+ public SourcesQuery setColor(boolean color) {
+ this.color = color;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ String url = Utils.getServerApiUrl() + "/sources?resource=" + getResourceKey() + "&";
+ if (length > 0) {
+ url += "from=" + from + "&to=" + (from + length) + "&";
+ }
+ if (color) {
+ url += "color=true&";
+ }
+ return url;
+ }
+
+ @Override
+ public void execute(QueryCallBack<FileSource> callback) {
+ JsonUtils.requestJson(this.toString(), new JSONHandlerDispatcher<FileSource>(callback) {
+ @Override
+ public FileSource parseResponse(JavaScriptObject obj) {
+ return parseLines(obj);
+ }
+ });
+ }
+
+ private FileSource parseLines(JavaScriptObject obj) {
+ Map<Integer, String> sourceLines = new TreeMap<Integer, String>();
+ FileSource src = new FileSource(sourceLines);
+ JSONArray jsonArray = new JSONArray(obj);
+ if (jsonArray.size() == 0) return src;
+ JSONObject sources = jsonArray.get(0).isObject();
+ if (sources.size() == 0) return src;
+ int maxSize = new Double(Math.pow(2, 16)).intValue();
+ int currentLine = from == 0 ? 1 : from;
+ while (currentLine < maxSize) {
+ JSONValue line = sources.get(Integer.toString(currentLine));
+ if (line == null) {
+ break;
+ }
+ sourceLines.put(currentLine++, line.isString().stringValue());
+ }
+ return src;
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Violation.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Violation.java
new file mode 100644
index 00000000000..6c9eae2ea87
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Violation.java
@@ -0,0 +1,80 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+public class Violation {
+
+ private String message;
+ private String priority;
+ private int line;
+ private Rule rule;
+ private Resource resource;
+
+ public Violation(String message, String priority, int line, Rule rule, Resource resource) {
+ this.message = message;
+ this.priority = priority;
+ this.line = line;
+ this.rule = rule;
+ this.resource = resource;
+ }
+
+ public Violation() {
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public String getPriority() {
+ return priority;
+ }
+
+ public void setPriority(String priority) {
+ this.priority = priority;
+ }
+
+ public int getLine() {
+ return line;
+ }
+
+ public void setLine(int line) {
+ this.line = line;
+ }
+
+ public Rule getRule() {
+ return rule;
+ }
+
+ public void setRule(Rule rule) {
+ this.rule = rule;
+ }
+
+ public Resource getResource() {
+ return resource;
+ }
+
+ public void setResource(Resource resource) {
+ this.resource = resource;
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Violations.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Violations.java
new file mode 100644
index 00000000000..d525c5bf634
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/Violations.java
@@ -0,0 +1,87 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class Violations extends ResponsePOJO {
+ private List<Violation> violations;
+ private Map<Integer, List<Violation>> byLines;
+
+ public Violations(List<Violation> violations) {
+ this.violations = violations;
+ }
+
+ public Violations() {
+ this.violations = new ArrayList<Violation>();
+ }
+
+ public void add(Violation v) {
+ violations.add(v);
+ byLines = null;
+ }
+
+ public List<Violation> getAll() {
+ return violations;
+ }
+
+
+ public Map<Integer, List<Violation>> getByLines() {
+ if (byLines == null) {
+ byLines = new HashMap<Integer, List<Violation>>();
+ for (Violation violation : violations) {
+ List<Violation> lineViolations = byLines.get(violation.getLine());
+ if (lineViolations == null) {
+ lineViolations = new ArrayList<Violation>();
+ byLines.put(violation.getLine(), lineViolations);
+ }
+ lineViolations.add(violation);
+ }
+ }
+ return byLines;
+ }
+
+ public String getLevelForLine(Integer line) {
+ List<Violation> lineViolations = getByLines().get(line);
+ String level = "";
+ if (lineViolations != null) {
+ for (Violation lineViolation : lineViolations) {
+ if ("BLOCKER".equals(lineViolation.getPriority()) || "CRITICAL".equals(lineViolation.getPriority()) || "MAJOR".equals(lineViolation.getPriority())) {
+ level = "error";
+
+ } else if (!"error".equals(level)) {
+ level = "warning";
+ }
+ }
+ }
+ return level;
+ }
+
+ public int countForLine(Integer line) {
+ List<Violation> lineViolations = getByLines().get(line);
+ if (lineViolations == null) {
+ return 0;
+ }
+ return lineViolations.size();
+ }
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/ViolationsQuery.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/ViolationsQuery.java
new file mode 100644
index 00000000000..46442b6c995
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/ViolationsQuery.java
@@ -0,0 +1,162 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.json.client.JSONArray;
+import com.google.gwt.json.client.JSONObject;
+import com.google.gwt.json.client.JSONString;
+import com.google.gwt.json.client.JSONValue;
+import org.sonar.api.web.gwt.client.Utils;
+
+public final class ViolationsQuery extends AbstractResourceQuery<Violations> {
+
+ private String scopes;
+ private String qualifiers;
+ private String rules;
+ private String categories;
+ private String priorities;
+ private Integer depth;
+
+ private ViolationsQuery(String resourceKey) {
+ super(resourceKey);
+ }
+
+ public static ViolationsQuery create(String resourceKey) {
+ return new ViolationsQuery(resourceKey);
+ }
+
+ public String getScopes() {
+ return scopes;
+ }
+
+ public ViolationsQuery setScopes(String scopes) {
+ this.scopes = scopes;
+ return this;
+ }
+
+ public String getQualifiers() {
+ return qualifiers;
+ }
+
+ public ViolationsQuery setQualifiers(String qualifiers) {
+ this.qualifiers = qualifiers;
+ return this;
+ }
+
+ public String getRules() {
+ return rules;
+ }
+
+ public ViolationsQuery setRules(String rules) {
+ this.rules = rules;
+ return this;
+ }
+
+ public String getCategories() {
+ return categories;
+ }
+
+ public ViolationsQuery setCategories(String s) {
+ this.categories = s;
+ return this;
+ }
+
+ public Integer getDepth() {
+ return depth;
+ }
+
+ public ViolationsQuery setDepth(Integer depth) {
+ this.depth = depth;
+ return this;
+ }
+
+ public String getPriorities() {
+ return priorities;
+ }
+
+ public ViolationsQuery setPriorities(String priorities) {
+ this.priorities = priorities;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ String url = Utils.getServerApiUrl() + "/violations?resource=" + getResourceKey() + "&";
+ if (depth != null) {
+ url += "depth=" + depth + "&";
+ }
+ if (scopes != null) {
+ url += "scopes=" + scopes + "&";
+ }
+ if (qualifiers != null) {
+ url += "qualifiers=" + qualifiers + "&";
+ }
+ if (rules != null) {
+ url += "rules=" + rules + "&";
+ }
+ if (categories != null) {
+ url += "categories=" + categories + "&";
+ }
+ if (priorities != null) {
+ url += "priorities=" + priorities + "&";
+ }
+ return url;
+ }
+
+ @Override
+ public void execute(final QueryCallBack<Violations> callback) {
+ JsonUtils.requestJson(this.toString(), new JSONHandlerDispatcher<Violations>(callback) {
+ @Override
+ public Violations parseResponse(JavaScriptObject obj) {
+ return parseJSON(obj);
+ }
+ });
+ }
+
+ private Violations parseJSON(JavaScriptObject obj) {
+ Violations result = new Violations();
+ JSONArray jsonArray = new JSONArray(obj);
+ for (int i = 0; i < jsonArray.size(); i++) {
+ JSONObject jsViolation = jsonArray.get(i).isObject();
+ if (jsViolation == null) {
+ continue;
+ }
+ JSONString message = jsViolation.get("message").isString();
+ JSONString priority = jsViolation.get("priority").isString();
+ JSONValue lineJson = jsViolation.get("line");
+ int lineIndex = 0;
+ if (lineJson != null) {
+ lineIndex = (int) lineJson.isNumber().doubleValue();
+ }
+
+ JSONObject ruleObj = jsViolation.get("rule").isObject();
+ Rule rule = new Rule(
+ JsonUtils.getString(ruleObj, "key"),
+ JsonUtils.getString(ruleObj, "name"),
+ JsonUtils.getString(ruleObj, "category")
+ );
+
+ result.add(new Violation(message.stringValue(), priority.stringValue(), lineIndex, rule, null));
+ }
+ return result;
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/VoidResponse.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/VoidResponse.java
new file mode 100644
index 00000000000..9b8879f5c04
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/VoidResponse.java
@@ -0,0 +1,24 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+public class VoidResponse extends ResponsePOJO {
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/WSMetrics.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/WSMetrics.java
new file mode 100644
index 00000000000..b12e253fd2f
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/webservices/WSMetrics.java
@@ -0,0 +1,243 @@
+/*
+ * 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.web.gwt.client.webservices;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class WSMetrics {
+
+ private WSMetrics() {
+ }
+
+ private final static List<Metric> DICTIONNARY = new ArrayList<Metric>();
+
+ public static final Metric NCLOC = add(new Metric("ncloc"));
+ public static final Metric LINES = add(new Metric("lines"));
+ public static final Metric CLASSES = add(new Metric("classes"));
+ public static final Metric PACKAGES = add(new Metric("packages"));
+ public static final Metric FUNCTIONS = add(new Metric("functions"));
+ public static final Metric ACCESSORS = add(new Metric("accessors"));
+ public static final Metric FILES = add(new Metric("files"));
+ public static final Metric DIRECTORIES = add(new Metric("directories"));
+ public static final Metric PUBLIC_API = add(new Metric("public_api"));
+
+ /* complexity */
+ public static final Metric COMPLEXITY = add(new Metric("complexity"));
+ public static final Metric CLASS_COMPLEXITY = add(new Metric("class_complexity"));
+ public static final Metric FUNCTION_COMPLEXITY = add(new Metric("function_complexity"));
+ public static final Metric FILE_COMPLEXITY = add(new Metric("file_complexity"));
+ public static final Metric STATEMENTS = add(new Metric("statements"));
+
+ public static final Metric CLASS_COMPLEXITY_DISTRIBUTION = add(new Metric("class_complexity_distribution"));
+ public static final Metric FUNCTION_COMPLEXITY_DISTRIBUTION = add(new Metric("function_complexity_distribution"));
+
+ /* comments */
+ public static final Metric COMMENT_LINES = add(new Metric("comment_lines"));
+ public static final Metric COMMENT_LINES_DENSITY = add(new Metric("comment_lines_density"));
+ public static final Metric PUBLIC_DOCUMENTED_API_DENSITY = add(new Metric("public_documented_api_density"));
+ public static final Metric PUBLIC_UNDOCUMENTED_API = add(new Metric("public_undocumented_api"));
+ public static final Metric COMMENTED_OUT_CODE_LINES = add(new Metric("commented_out_code_lines"));
+
+ /* unit tests */
+ public static final Metric TESTS = add(new Metric("tests"));
+ public static final Metric TESTS_EXECUTION_TIME = add(new Metric("test_execution_time"));
+ public static final Metric TEST_ERRORS = add(new Metric("test_errors"));
+ public static final Metric SKIPPED_TESTS = add(new Metric("skipped_tests"));
+ public static final Metric TEST_FAILURES = add(new Metric("test_failures"));
+ public static final Metric TEST_SUCCESS_DENSITY = add(new Metric("test_success_density"));
+ public static final Metric TEST_DATA = add(new Metric("test_data"));
+
+ /* coverage */
+ public static final Metric COVERAGE = add(new Metric("coverage"));
+ public static final Metric LINE_COVERAGE = add(new Metric("line_coverage"));
+ public static final Metric UNCOVERED_LINES = add(new Metric("uncovered_lines"));
+ public static final Metric BRANCH_COVERAGE = add(new Metric("branch_coverage"));
+ public static final Metric UNCOVERED_CONDITIONS = add(new Metric("uncovered_conditions"));
+ public static final Metric COVERAGE_LINE_HITS_DATA = add(new Metric("coverage_line_hits_data"));
+ public static final Metric BRANCH_COVERAGE_HITS_DATA = add(new Metric("branch_coverage_hits_data"));
+
+ /* duplicated lines */
+ public static final Metric DUPLICATED_LINES = add(new Metric("duplicated_lines"));
+ public static final Metric DUPLICATED_BLOCKS = add(new Metric("duplicated_blocks"));
+ public static final Metric DUPLICATED_FILES = add(new Metric("duplicated_files"));
+ public static final Metric DUPLICATED_LINES_DENSITY = add(new Metric("duplicated_lines_density"));
+ public static final Metric DUPLICATIONS_DATA = add(new Metric("duplications_data"));
+
+ /* coding rules */
+ public static final Metric VIOLATIONS_DENSITY = add(new Metric("violations_density"));
+ public static final Metric VIOLATIONS = add(new Metric("violations"));
+ public static final Metric WEIGHTED_VIOLATIONS = add(new Metric("weighted_violations"));
+
+ /* design */
+ public static final Metric LCOM4 = add(new Metric("lcom4"));
+ public static final Metric RFC = add(new Metric("rfc"));
+
+ public static class MetricsList extends ResponsePOJO {
+
+ private List<Metric> metrics = new ArrayList<Metric>();
+
+ public List<Metric> getMetrics() {
+ return metrics;
+ }
+ }
+
+ /**
+ * Generates a callback that will update the metrics definitions from the WSMetrics metrics constants list with data
+ * received from a MetricsQuery call
+ *
+ * @param callback
+ * @return
+ */
+ public static QueryCallBack<MetricsList> getUpdateMetricsFromServer(final QueryCallBack<MetricsList> callback) {
+ return new QueryCallBack<MetricsList>() {
+ public void onResponse(MetricsList response, JavaScriptObject jsonRawResponse) {
+ for (Metric metric : response.getMetrics()) {
+ Metric WSMetricConstant = get(metric.getKey());
+ if (WSMetricConstant != null) {
+ WSMetricConstant.updateFrom(metric);
+ } else {
+ add(metric);
+ }
+ }
+ callback.onResponse(response, jsonRawResponse);
+ }
+
+ public void onError(int errorCode, String errorMessage) {
+ callback.onError(errorCode, errorMessage);
+ }
+
+ public void onTimeout() {
+ callback.onTimeout();
+ }
+ };
+ }
+
+ public static class Metric {
+ public enum ValueType {
+ INT, FLOAT, PERCENT, BOOL, STRING, MILLISEC, DATA, LEVEL, DISTRIB
+ }
+
+ private String key;
+ private String name;
+ private String description;
+ private String domain;
+ private boolean qualitative;
+ private boolean userManaged;
+ private int direction;
+ private ValueType type;
+
+ public Metric(String key) {
+ super();
+ this.key = key;
+ }
+
+ public Metric(String key, String name, String description, String domain,
+ boolean qualitative, boolean userManaged, int direction, ValueType type) {
+ super();
+ this.key = key;
+ this.name = name;
+ this.description = description;
+ this.domain = domain;
+ this.qualitative = qualitative;
+ this.userManaged = userManaged;
+ this.direction = direction;
+ this.type = type;
+ }
+
+ public void updateFrom(Metric metric) {
+ this.name = metric.getName();
+ this.description = metric.getDescription();
+ this.domain = metric.getDomain();
+ this.qualitative = metric.isQualitative();
+ this.userManaged = metric.isUserManaged();
+ this.direction = metric.getDirection();
+ this.type = metric.getType();
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public ValueType getType() {
+ return type;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public String getDomain() {
+ return domain;
+ }
+
+ public boolean isQualitative() {
+ return qualitative;
+ }
+
+ public boolean isUserManaged() {
+ return userManaged;
+ }
+
+ public int getDirection() {
+ return direction;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public int hashCode() {
+ return key.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Metric)) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ Metric other = (Metric) obj;
+ return key.equals(other.getKey());
+ }
+ }
+
+ public static Metric add(Metric metric) {
+ if (!DICTIONNARY.contains(metric)) {
+ DICTIONNARY.add(metric);
+ }
+ return metric;
+ }
+
+ public static Metric get(String metricKey) {
+ for (Metric metric : DICTIONNARY) {
+ if (metric.getKey().equals(metricKey)) {
+ return metric;
+ }
+ }
+ return new Metric(metricKey);
+ }
+
+}
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/widgets/LoadingLabel.java b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/widgets/LoadingLabel.java
new file mode 100644
index 00000000000..72d45bea4df
--- /dev/null
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/web/gwt/client/widgets/LoadingLabel.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.web.gwt.client.widgets;
+
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.HTML;
+
+public class LoadingLabel extends Composite {
+ public LoadingLabel() {
+ this("loading...");
+ getElement().setId("loading");
+ }
+
+ public LoadingLabel(String text) {
+ initWidget(new HTML("<div class='loading'>" + text + "</div>"));
+ }
+}