aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/sonar-core-plugin
diff options
context:
space:
mode:
authorsimonbrandhof <simon.brandhof@gmail.com>2010-09-06 14:08:06 +0000
committersimonbrandhof <simon.brandhof@gmail.com>2010-09-06 14:08:06 +0000
commitaeadc1f9129274949daaa57738c7c4550bdfbc7b (patch)
tree08dadf5ef7474fc41d1d48f74648f1ba8b55f34d /plugins/sonar-core-plugin
downloadsonarqube-aeadc1f9129274949daaa57738c7c4550bdfbc7b.tar.gz
sonarqube-aeadc1f9129274949daaa57738c7c4550bdfbc7b.zip
SONAR-236 remove deprecated code from checkstyle plugin + display default value of rule parameters in Q profile console
Diffstat (limited to 'plugins/sonar-core-plugin')
-rwxr-xr-xplugins/sonar-core-plugin/GWT-Clouds.sh1
-rwxr-xr-xplugins/sonar-core-plugin/GWT-CoverageViewer.sh1
-rwxr-xr-xplugins/sonar-core-plugin/GWT-DefaultSourceViewer.sh1
-rwxr-xr-xplugins/sonar-core-plugin/GWT-DuplicationsViewer.sh1
-rwxr-xr-xplugins/sonar-core-plugin/GWT-Hotspots.sh1
-rwxr-xr-xplugins/sonar-core-plugin/GWT-TestsViewer.sh1
-rw-r--r--plugins/sonar-core-plugin/pom.xml137
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java191
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/batch/ExcludedResourceFilter.java55
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/charts/DistributionAreaChart.java83
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/charts/DistributionBarChart.java129
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/charts/XradarChart.java86
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/Clouds.java42
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/Calculator.java119
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/GwtClouds.java197
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/model/CloudElement.java53
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/model/Color.java50
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/widget/ClassCloudsWidget.java146
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/widget/TabWidget.java77
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/colorizers/JavaColorizerFormat.java39
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/coverageviewer/CoverageViewerDefinition.java40
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/coverageviewer/client/CoveragePanel.java123
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/coverageviewer/client/CoverageViewer.java64
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/defaultsourceviewer/DefaultSourceViewer.java39
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/defaultsourceviewer/client/GwtDefaultSourceViewer.java113
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/duplicationsviewer/DuplicationsViewerDefinition.java41
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsPanel.java161
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsViewer.java79
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/Hotspots.java41
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/GwtHotspots.java64
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/I18nConstants.java69
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/AbstractHotspot.java124
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MetricHotspot.java133
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostBadlyDesignedFiles.java127
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedResources.java127
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedRules.java149
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/metrics/UserManagedMetrics.java36
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeDeletedResources.java47
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeDeprecatedLast.java47
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeDisabledResources.java62
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeEntities.java53
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeEventOrphans.java43
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeOrphanResources.java47
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgePropertyOrphans.java63
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeResourceRoles.java54
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeRuleMeasures.java56
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeUnprocessed.java52
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/UnflagLastDoublons.java48
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/security/ApplyProjectRolesDecorator.java71
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/security/RoleManager.java89
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/AbstractCoverageDecorator.java69
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/AlertUtils.java104
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/AsynchronousMeasuresSensor.java56
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/BranchCoverageDecorator.java54
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CheckAlertThresholds.java116
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CommentDensityDecorator.java91
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CoverageDecorator.java58
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/DirectoriesDecorator.java68
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/FilesDecorator.java64
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/GenerateAlertEvents.java92
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/JavaSourceImporter.java47
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/LineCoverageDecorator.java54
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ProfileSensor.java55
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ProjectLinksSensor.java81
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/TendencyAnalyser.java191
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/TendencyDecorator.java112
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/UncoveredComplexityDecorator.java74
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/UnitTestDecorator.java94
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/VersionEventsSensor.java74
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ViolationsDecorator.java160
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ViolationsDensityDecorator.java140
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/WeightedViolationsDecorator.java111
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/testdetailsviewer/TestsViewerDefinition.java41
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/testdetailsviewer/client/TestsPanel.java168
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/testdetailsviewer/client/TestsViewer.java87
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/GwtPageSelector.java30
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/client/I18nConstants.java30
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PageDef.java94
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PagePanel.java156
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PageSelector.java224
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/violationsviewer/ViolationsViewerDefinition.java41
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/violationsviewer/client/I18nConstants.java39
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/violationsviewer/client/ViolationsPanel.java155
-rw-r--r--plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/violationsviewer/client/ViolationsViewer.java206
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/clouds/GwtClouds.gwt.xml11
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/clouds/public/clouds.css29
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/clouds/public/test.html39
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/coverageviewer/CoverageViewer.gwt.xml10
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/coverageviewer/public/test.html41
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/defaultsourceviewer/GwtDefaultSourceViewer.gwt.xml10
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/defaultsourceviewer/public/test.html26
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/duplicationsviewer/DuplicationsViewer.gwt.xml13
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/duplicationsviewer/public/DuplicationsViewer.css48
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/duplicationsviewer/public/test.html43
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/hotspots/GwtHotspots.gwt.xml15
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/hotspots/client/I18nConstants_fr.properties16
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/hotspots/public/hotspots.css102
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/hotspots/public/test.html36
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/TestsViewer.gwt.xml13
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/TestsViewer.css88
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/error.pngbin0 -> 701 bytes
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/failure.pngbin0 -> 666 bytes
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/ok.pngbin0 -> 781 bytes
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/skipped.pngbin0 -> 786 bytes
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/test.html41
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/ui/pageselector/PageSelector.gwt.xml14
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/ui/pageselector/client/I18nConstants_fr.properties2
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/ui/pageselector/public/pageselector.css25
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/ui/pageselector/public/test.html41
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/violationsviewer/ViolationsViewer.gwt.xml13
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/violationsviewer/client/I18nConstants_fr.properties5
-rw-r--r--plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/violationsviewer/public/test.html41
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/CorePluginTest.java33
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/batch/ExcludedResourceFilterTest.java69
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/charts/DistributionAreaChartTest.java63
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/charts/DistributionBarChartTest.java97
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/charts/XradarChartTest.java50
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/metrics/UserManagedMetricsTest.java33
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeDeletedResourcesTest.java44
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeDeprecatedLastTest.java37
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest.java49
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeEntitiesTest.java36
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeEventOrphansTest.java40
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeOrphanResourcesTest.java38
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgePropertyOrphansTest.java42
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeResourceRolesTest.java38
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeRuleMeasuresTest.java43
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeUnprocessedTest.java36
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/UnflagLastDoublonsTest.java38
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/security/ApplyProjectRolesDecoratorTest.java78
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/security/RoleManagerTest.java55
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/AlertUtilsTest.java253
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/BranchCoverageDecoratorTest.java68
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CheckAlertThresholdsTest.java167
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CommentDensityDecoratorTest.java76
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CoverageDecoratorTest.java104
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/DirectoriesDecoratorTest.java94
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/GenerateAlertEventsTest.java149
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/JavaSourceImporterTest.java68
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/LineCoverageDecoratorTest.java99
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ProfileSensorTest.java44
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ProjectLinksSensorTest.java89
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/TendencyAnalyserTest.java131
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/TendencyDecoratorTest.java114
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/UncoveredComplexityDecoratorTest.java63
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/UnitTestDecoratorTest.java45
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/VersionEventsSensorTest.java142
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ViolationsDecoratorTest.java163
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ViolationsDensityDecoratorTest.java173
-rw-r--r--plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/WeightedViolationsDecoratorTest.java116
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeletedResourcesTest/purgeDeletedResources-result.xml68
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeletedResourcesTest/purgeDeletedResources.xml68
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeletedResourcesTest/sharedFixture.xml33
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeprecatedLastTest/purgeDeprecatedLast-result.xml71
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeprecatedLastTest/purgeDeprecatedLast.xml71
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeprecatedLastTest/sharedFixture.xml33
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/nothingToPurge-result.xml89
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/nothingToPurge.xml89
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/purgeDisabledModule-result.xml75
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/purgeDisabledModule.xml78
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/purgeDisabledProject-result.xml106
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/purgeDisabledProject.xml112
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/sharedFixture.xml9
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEntitiesTest/purgeEntities-result.xml141
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEntitiesTest/purgeEntities.xml141
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEntitiesTest/sharedFixture.xml33
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEventOrphansTest/purgeEventOrphans-result.xml16
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEventOrphansTest/purgeEventOrphans.xml16
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeOrphanResourcesTest/purgeOrphanResources-result.xml23
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeOrphanResourcesTest/purgeOrphanResources.xml23
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgePropertyOrphansTest/purgeResourceOrphans-result.xml13
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgePropertyOrphansTest/purgeResourceOrphans.xml11
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgePropertyOrphansTest/purgeUserOrphans-result.xml10
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgePropertyOrphansTest/purgeUserOrphans.xml8
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeResourceRolesTest/purgeResourceRoles-result.xml27
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeResourceRolesTest/purgeResourceRoles.xml27
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeRuleMeasuresTest/purgeRuleMeasures-result.xml91
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeRuleMeasuresTest/purgeRuleMeasures.xml92
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeRuleMeasuresTest/sharedFixture.xml28
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeUnprocessedTest/purgeUnprocessed-result.xml141
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeUnprocessedTest/purgeUnprocessed.xml141
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeUnprocessedTest/sharedFixture.xml33
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/UnflagLastDoublonsTest/sharedFixture.xml34
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/UnflagLastDoublonsTest/unflagLastDoublons-result.xml68
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/UnflagLastDoublonsTest/unflagLastDoublons.xml68
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectAnyoneDefaultRoleToResource-result.xml21
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectAnyoneDefaultRoleToResource.xml16
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectDefaultRolesToResource-result.xml39
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectDefaultRolesToResource.xml31
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectZeroDefaultRolesToResource-result.xml27
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectZeroDefaultRolesToResource.xml27
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/sharedFixture.xml7
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/ProjectLinksSensorTest/shouldDeleteMissingLinks.xml10
-rw-r--r--plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/ProjectLinksSensorTest/shouldSaveLinks.xml51
194 files changed, 13023 insertions, 0 deletions
diff --git a/plugins/sonar-core-plugin/GWT-Clouds.sh b/plugins/sonar-core-plugin/GWT-Clouds.sh
new file mode 100755
index 00000000000..a6746040a6f
--- /dev/null
+++ b/plugins/sonar-core-plugin/GWT-Clouds.sh
@@ -0,0 +1 @@
+mvn gwt:run -DrunTarget=org.sonar.plugins.core.clouds.GwtClouds/test.html \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/GWT-CoverageViewer.sh b/plugins/sonar-core-plugin/GWT-CoverageViewer.sh
new file mode 100755
index 00000000000..fccc7aa3f4a
--- /dev/null
+++ b/plugins/sonar-core-plugin/GWT-CoverageViewer.sh
@@ -0,0 +1 @@
+mvn gwt:run -DrunTarget=org.sonar.plugins.core.coverageviewer.CoverageViewer/test.html \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/GWT-DefaultSourceViewer.sh b/plugins/sonar-core-plugin/GWT-DefaultSourceViewer.sh
new file mode 100755
index 00000000000..a8f956909e1
--- /dev/null
+++ b/plugins/sonar-core-plugin/GWT-DefaultSourceViewer.sh
@@ -0,0 +1 @@
+mvn gwt:run -DrunTarget=org.sonar.plugins.core.defaultsourceviewer.GwtDefaultSourceViewer/test.html \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/GWT-DuplicationsViewer.sh b/plugins/sonar-core-plugin/GWT-DuplicationsViewer.sh
new file mode 100755
index 00000000000..28d6abee29d
--- /dev/null
+++ b/plugins/sonar-core-plugin/GWT-DuplicationsViewer.sh
@@ -0,0 +1 @@
+mvn gwt:run -DrunTarget=org.sonar.plugins.core.duplicationsviewer.DuplicationsViewer/test.html \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/GWT-Hotspots.sh b/plugins/sonar-core-plugin/GWT-Hotspots.sh
new file mode 100755
index 00000000000..a659c47d15e
--- /dev/null
+++ b/plugins/sonar-core-plugin/GWT-Hotspots.sh
@@ -0,0 +1 @@
+mvn gwt:run -DrunTarget=org.sonar.plugins.core.hotspots.GwtHotspots/test.html \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/GWT-TestsViewer.sh b/plugins/sonar-core-plugin/GWT-TestsViewer.sh
new file mode 100755
index 00000000000..2ee40ce95a0
--- /dev/null
+++ b/plugins/sonar-core-plugin/GWT-TestsViewer.sh
@@ -0,0 +1 @@
+mvn gwt:run -DrunTarget=org.sonar.plugins.core.testdetailsviewer.TestsViewer/test.html \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/pom.xml b/plugins/sonar-core-plugin/pom.xml
new file mode 100644
index 00000000000..d9aeef72496
--- /dev/null
+++ b/plugins/sonar-core-plugin/pom.xml
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar</artifactId>
+ <version>2.3-SNAPSHOT</version>
+ <relativePath>../..</relativePath>
+ </parent>
+ <groupId>org.codehaus.sonar.plugins</groupId>
+ <artifactId>sonar-core-plugin</artifactId>
+ <packaging>sonar-plugin</packaging>
+ <name>Sonar :: Plugins :: Core</name>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-plugin-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-gwt-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-project</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-plugin-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.gwt</groupId>
+ <artifactId>gwt-user</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.gwt</groupId>
+ <artifactId>gwt-incubator</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <!-- unit tests -->
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-testing-harness</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <testResources>
+ <testResource>
+ <directory>${basedir}/src/main/resources</directory>
+ </testResource>
+ <testResource>
+ <directory>${basedir}/src/test/resources</directory>
+ </testResource>
+ </testResources>
+
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-packaging-maven-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <pluginKey>core</pluginKey>
+ <pluginName>Core</pluginName>
+ <pluginClass>org.sonar.plugins.core.CorePlugin</pluginClass>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <forkMode>always</forkMode>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>com.atlassian.maven.plugins</groupId>
+ <artifactId>maven-clover2-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <!-- GWT classes -->
+ <exclude>**/client/**/*.java</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>gwt-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <configuration>
+ <modules>
+ <module>org.sonar.plugins.core.ui.pageselector.PageSelector</module>
+ <module>org.sonar.plugins.core.clouds.GwtClouds</module>
+ <module>org.sonar.plugins.core.violationsviewer.ViolationsViewer</module>
+ <module>org.sonar.plugins.core.coverageviewer.CoverageViewer</module>
+ <module>org.sonar.plugins.core.defaultsourceviewer.GwtDefaultSourceViewer</module>
+ <module>org.sonar.plugins.core.duplicationsviewer.DuplicationsViewer</module>
+ <module>org.sonar.plugins.core.testdetailsviewer.TestsViewer</module>
+ <module>org.sonar.plugins.core.hotspots.GwtHotspots</module>
+ </modules>
+ <skip>${skipGwt}</skip>
+ <webappDirectory>${project.build.directory}/classes</webappDirectory>
+
+ <!-- do not break on two lines -->
+ <extraJvmArgs>-Xmx512m -Dgwt.jjs.permutationWorkerFactory=com.google.gwt.dev.ThreadedPermutationWorkerFactory</extraJvmArgs>
+ </configuration>
+ <goals>
+ <goal>compile</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+
+ </plugins>
+ </build>
+</project> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
new file mode 100644
index 00000000000..d65ededd9a4
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
@@ -0,0 +1,191 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core;
+
+import org.sonar.api.*;
+import org.sonar.api.checks.NoSonarFilter;
+import org.sonar.api.resources.Java;
+import org.sonar.plugins.core.batch.ExcludedResourceFilter;
+import org.sonar.plugins.core.charts.DistributionAreaChart;
+import org.sonar.plugins.core.charts.DistributionBarChart;
+import org.sonar.plugins.core.charts.XradarChart;
+import org.sonar.plugins.core.clouds.Clouds;
+import org.sonar.plugins.core.colorizers.JavaColorizerFormat;
+import org.sonar.plugins.core.coverageviewer.CoverageViewerDefinition;
+import org.sonar.plugins.core.defaultsourceviewer.DefaultSourceViewer;
+import org.sonar.plugins.core.duplicationsviewer.DuplicationsViewerDefinition;
+import org.sonar.plugins.core.hotspots.Hotspots;
+import org.sonar.plugins.core.metrics.UserManagedMetrics;
+import org.sonar.plugins.core.purges.*;
+import org.sonar.plugins.core.security.ApplyProjectRolesDecorator;
+import org.sonar.plugins.core.sensors.*;
+import org.sonar.plugins.core.testdetailsviewer.TestsViewerDefinition;
+import org.sonar.plugins.core.ui.pageselector.GwtPageSelector;
+import org.sonar.plugins.core.violationsviewer.ViolationsViewerDefinition;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Properties({
+ @Property(
+ key = CoreProperties.CORE_COVERAGE_PLUGIN_PROPERTY,
+ defaultValue = "cobertura",
+ name = "Code coverage plugin",
+ description = "Key of the code coverage plugin to use.",
+ project = true,
+ global = true),
+ @Property(
+ key = CoreProperties.CORE_IMPORT_SOURCES_PROPERTY,
+ defaultValue = "" + CoreProperties.CORE_IMPORT_SOURCES_DEFAULT_VALUE,
+ name = "Import sources",
+ description = "Set to false if sources should not be displayed, e.g. for security reasons.",
+ project = true,
+ module = true,
+ global = true),
+ @Property(
+ key = CoreProperties.CORE_TENDENCY_DEPTH_PROPERTY,
+ defaultValue = "" + CoreProperties.CORE_TENDENCY_DEPTH_DEFAULT_VALUE,
+ name = "Tendency period",
+ description = TendencyDecorator.PROP_DAYS_DESCRIPTION,
+ project = false,
+ global = true),
+ @Property(
+ key = CoreProperties.CORE_SKIPPED_MODULES_PROPERTY,
+ name = "Exclude modules",
+ description = "Maven artifact ids of modules to exclude (comma-separated).",
+ project = true,
+ global = false),
+ @Property(
+ key = CoreProperties.CORE_RULE_WEIGHTS_PROPERTY,
+ defaultValue = CoreProperties.CORE_RULE_WEIGHTS_DEFAULT_VALUE,
+ name = "Rules weight",
+ description = "A weight is associated to each priority to calculate the Rules Compliance Index.",
+ project = false,
+ global = true),
+ @Property(
+ key = CoreProperties.CORE_FORCE_AUTHENTICATION_PROPERTY,
+ defaultValue = "" + CoreProperties.CORE_FORCE_AUTHENTICATION_DEFAULT_VALUE,
+ name = "Force user authentication",
+ description = "Forcing user authentication stops un-logged users to access Sonar.",
+ project = false,
+ global = true),
+ @Property(
+ key = CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_PROPERTY,
+ defaultValue = "" + CoreProperties.CORE_ALLOW_USERS_TO_SIGNUP_DEAULT_VALUE,
+ name = "Allow users to sign up online",
+ description = "Users can sign up online.",
+ project = false,
+ global = true),
+ @Property(
+ key = CoreProperties.CORE_DEFAULT_GROUP,
+ defaultValue = CoreProperties.CORE_DEFAULT_GROUP_DEFAULT_VALUE,
+ name = "Default user group",
+ description = "Any new users will automatically join this group.",
+ project = false,
+ global = true
+ )
+})
+public class CorePlugin implements Plugin {
+
+ public String getKey() {
+ return CoreProperties.CORE_PLUGIN;
+ }
+
+ public String getName() {
+ return "General";
+ }
+
+ public String getDescription() {
+ return "";
+ }
+
+ public List getExtensions() {
+ List extensions = new ArrayList();
+
+ // languages
+ extensions.add(Java.class);
+
+ // metrics
+ extensions.add(UserManagedMetrics.class);
+
+ // pages
+ extensions.add(GwtPageSelector.class);
+ extensions.add(DefaultSourceViewer.class);
+ extensions.add(CoverageViewerDefinition.class);
+ extensions.add(ViolationsViewerDefinition.class);
+ extensions.add(DuplicationsViewerDefinition.class);
+ extensions.add(TestsViewerDefinition.class);
+ extensions.add(Clouds.class);
+ extensions.add(Hotspots.class);
+
+ // chart
+ extensions.add(XradarChart.class);
+ extensions.add(DistributionBarChart.class);
+ extensions.add(DistributionAreaChart.class);
+
+ // colorizers
+ extensions.add(JavaColorizerFormat.class);
+
+ // batch
+ extensions.add(JavaSourceImporter.class);
+ extensions.add(ProfileSensor.class);
+ extensions.add(ProjectLinksSensor.class);
+ extensions.add(AsynchronousMeasuresSensor.class);
+ extensions.add(UnitTestDecorator.class);
+ extensions.add(VersionEventsSensor.class);
+ extensions.add(CheckAlertThresholds.class);
+ extensions.add(GenerateAlertEvents.class);
+ extensions.add(ViolationsDecorator.class);
+ extensions.add(WeightedViolationsDecorator.class);
+ extensions.add(ViolationsDensityDecorator.class);
+ extensions.add(TendencyDecorator.class);
+ extensions.add(LineCoverageDecorator.class);
+ extensions.add(CoverageDecorator.class);
+ extensions.add(BranchCoverageDecorator.class);
+ extensions.add(UncoveredComplexityDecorator.class);
+ extensions.add(ApplyProjectRolesDecorator.class);
+ extensions.add(ExcludedResourceFilter.class);
+ extensions.add(CommentDensityDecorator.class);
+ extensions.add(NoSonarFilter.class);
+ extensions.add(DirectoriesDecorator.class);
+ extensions.add(FilesDecorator.class);
+
+
+ // purges
+ extensions.add(PurgeOrphanResources.class);
+ extensions.add(PurgeEntities.class);
+ extensions.add(PurgeRuleMeasures.class);
+ extensions.add(PurgeUnprocessed.class);
+ extensions.add(PurgeDeletedResources.class);
+ extensions.add(PurgeDeprecatedLast.class);
+ extensions.add(UnflagLastDoublons.class);
+ extensions.add(PurgeDisabledResources.class);
+ extensions.add(PurgeResourceRoles.class);
+ extensions.add(PurgeEventOrphans.class);
+ extensions.add(PurgePropertyOrphans.class);
+
+ return extensions;
+ }
+
+ @Override
+ public String toString() {
+ return getKey();
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/batch/ExcludedResourceFilter.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/batch/ExcludedResourceFilter.java
new file mode 100644
index 00000000000..78365bce65c
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/batch/ExcludedResourceFilter.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.plugins.core.batch;
+
+import org.sonar.api.batch.ResourceFilter;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
+
+/**
+ * @since 1.12
+ */
+public class ExcludedResourceFilter implements ResourceFilter {
+
+ private String[] exclusionPatterns;
+
+ public ExcludedResourceFilter(Project project) {
+ this(project.getExclusionPatterns());
+ }
+
+ protected ExcludedResourceFilter(String[] exclusionPatterns) {
+ this.exclusionPatterns = (exclusionPatterns==null ? new String[0] : exclusionPatterns);
+ }
+
+ public boolean isIgnored(Resource resource) {
+ if (ResourceUtils.isUnitTestClass(resource)) {
+ // See SONAR-1115 Exclusion patterns do not apply to unit tests.
+ return false;
+ }
+
+ for (String pattern : exclusionPatterns) {
+ if (resource.matchFilePattern(pattern)) {
+ return true;
+ }
+ }
+ return false;
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/charts/DistributionAreaChart.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/charts/DistributionAreaChart.java
new file mode 100644
index 00000000000..a0f597ee7fc
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/charts/DistributionAreaChart.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.plugins.core.charts;
+
+import org.apache.commons.lang.StringUtils;
+import org.jfree.chart.axis.CategoryAxis;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.plot.CategoryPlot;
+import org.jfree.chart.plot.Plot;
+import org.jfree.chart.renderer.category.AreaRenderer;
+import org.jfree.data.category.DefaultCategoryDataset;
+import org.sonar.api.charts.AbstractChart;
+import org.sonar.api.charts.ChartParameters;
+
+import java.text.NumberFormat;
+
+public class DistributionAreaChart extends AbstractChart {
+ private static final String PARAM_COLORS = "c";
+
+ public String getKey() {
+ return "distarea";
+ }
+
+ @Override
+ protected Plot getPlot(ChartParameters params) {
+ DefaultCategoryDataset dataset = createDataset(params);
+
+ CategoryAxis domainAxis = new CategoryAxis();
+ domainAxis.setCategoryMargin(0.0);
+ domainAxis.setLowerMargin(0.0);
+ domainAxis.setUpperMargin(0.0);
+
+ NumberAxis rangeAxis = new NumberAxis();
+ rangeAxis.setNumberFormatOverride(NumberFormat.getIntegerInstance(params.getLocale()));
+ rangeAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
+
+ AreaRenderer renderer = new AreaRenderer();
+ CategoryPlot plot = new CategoryPlot(dataset, domainAxis, rangeAxis, renderer);
+ plot.setForegroundAlpha(0.5f);
+ plot.setDomainGridlinesVisible(true);
+ configureColors(dataset, plot, params.getValues(PARAM_COLORS, ","));
+ return plot;
+ }
+
+ private DefaultCategoryDataset createDataset(ChartParameters params) {
+ DefaultCategoryDataset dataset = new DefaultCategoryDataset();
+
+ String[] series = params.getValues("v", "|", true);
+ int index = 0;
+ while (index < series.length) {
+ String[] pairs = StringUtils.split(series[index], ";");
+ if (pairs.length == 0) {
+ dataset.addValue((Number)0.0, index, "0");
+
+ } else {
+ for (String pair : pairs) {
+ String[] keyValue = StringUtils.split(pair, "=");
+ double val = Double.parseDouble(keyValue[1]);
+ dataset.addValue((Number) val, index, keyValue[0]);
+ }
+ }
+ index++;
+ }
+ return dataset;
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/charts/DistributionBarChart.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/charts/DistributionBarChart.java
new file mode 100644
index 00000000000..6820283e795
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/charts/DistributionBarChart.java
@@ -0,0 +1,129 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.charts;
+
+import org.apache.commons.lang.StringUtils;
+import org.jfree.chart.axis.CategoryAxis;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.plot.CategoryPlot;
+import org.jfree.chart.plot.Plot;
+import org.jfree.chart.renderer.category.BarRenderer;
+import org.jfree.data.category.DefaultCategoryDataset;
+import org.sonar.api.charts.AbstractChart;
+import org.sonar.api.charts.ChartParameters;
+
+import java.awt.*;
+import java.text.DecimalFormat;
+
+public class DistributionBarChart extends AbstractChart {
+
+
+ public static final String PARAM_VALUES = "v";
+ public static final String PARAM_COLORS = "c";
+ public static final String PARAM_Y_SUFFIX = "ysuf";
+ public static final String PARAM_X_SUFFIX = "xsuf";
+ public static final String PARAM_FONT_SIZE = "fs";
+
+ public String getKey() {
+ return "distbar";
+ }
+
+ @Override
+ public Plot getPlot(ChartParameters params) {
+ CategoryPlot plot = generateJFreeChart(params);
+ plot.setOutlinePaint(OUTLINE_COLOR);
+ plot.setDomainGridlinePaint(GRID_COLOR);
+ plot.setRangeGridlinePaint(GRID_COLOR);
+ return plot;
+ }
+
+ private CategoryPlot generateJFreeChart(ChartParameters params) {
+ DefaultCategoryDataset dataset = new DefaultCategoryDataset();
+ CategoryPlot plot = new CategoryPlot();
+
+ Font font = getFont(params.getValue(PARAM_FONT_SIZE));
+ configureDomainAxis(plot, font);
+ configureRangeAxis(plot, params.getValue(PARAM_Y_SUFFIX, "", true), font);
+ configureRenderer(plot);
+ configureValues(dataset, params.getValues(PARAM_VALUES, "|", true), params.getValue(PARAM_X_SUFFIX, "", true));
+ configureColors(dataset, plot, params.getValues(PARAM_COLORS, ","));
+
+ plot.setDataset(dataset);
+ return plot;
+ }
+
+ private void configureValues(DefaultCategoryDataset dataset, String[] series, String xSuffix) {
+ int index = 0;
+ while (index < series.length) {
+ String[] pairs = StringUtils.split(series[index], ";");
+ if (pairs.length == 0) {
+ dataset.addValue((Number) 0.0, index, "0");
+
+ } else {
+ for (String pair : pairs) {
+ String[] keyValue = StringUtils.split(pair, "=");
+ double val = Double.parseDouble(keyValue[1]);
+ dataset.addValue((Number) val, index, keyValue[0] + xSuffix);
+ }
+ }
+ index++;
+ }
+
+ }
+
+ private void configureRenderer(CategoryPlot plot) {
+ BarRenderer renderer = new BarRenderer();
+ renderer.setDrawBarOutline(true);
+ renderer.setSeriesItemLabelsVisible(0, true);
+ renderer.setItemMargin(0);
+ plot.setRenderer(renderer);
+ }
+
+ private void configureDomainAxis(CategoryPlot plot, Font font) {
+ CategoryAxis categoryAxis = new CategoryAxis();
+ categoryAxis.setTickMarksVisible(true);
+ categoryAxis.setTickLabelFont(font);
+ categoryAxis.setTickLabelPaint(OUTLINE_COLOR);
+ plot.setDomainAxis(categoryAxis);
+ plot.setDomainGridlinesVisible(false);
+ }
+
+ private Font getFont(String fontSize) {
+ int size = FONT_SIZE;
+ if (!StringUtils.isBlank(fontSize)) {
+ size = Integer.parseInt(fontSize);
+ }
+ return new Font("SansSerif", Font.PLAIN, size);
+ }
+
+ private void configureRangeAxis(CategoryPlot plot, String valueLabelSuffix, Font font) {
+ NumberAxis numberAxis = new NumberAxis();
+ numberAxis.setUpperMargin(0.3);
+ numberAxis.setTickLabelFont(font);
+ numberAxis.setTickLabelPaint(OUTLINE_COLOR);
+ String suffix = "";
+ if (valueLabelSuffix != null && !"".equals(valueLabelSuffix)) {
+ suffix = new StringBuilder().append("'").append(valueLabelSuffix).append("'").toString();
+ }
+ numberAxis.setNumberFormatOverride(new DecimalFormat("0" + suffix));
+ numberAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
+ plot.setRangeAxis(numberAxis);
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/charts/XradarChart.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/charts/XradarChart.java
new file mode 100644
index 00000000000..52cfebd7d3b
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/charts/XradarChart.java
@@ -0,0 +1,86 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.charts;
+
+import org.apache.commons.lang.StringUtils;
+import org.jfree.chart.plot.Plot;
+import org.jfree.chart.plot.SpiderWebPlot;
+import org.jfree.data.category.CategoryDataset;
+import org.jfree.data.category.DefaultCategoryDataset;
+import org.sonar.api.charts.AbstractChart;
+import org.sonar.api.charts.ChartParameters;
+
+import java.awt.*;
+
+public class XradarChart extends AbstractChart {
+
+ /**
+ * see an example of complete URL in XradarChartTest
+ */
+
+ public static final String PARAM_COLOR = "c";
+ public static final String PARAM_MAX_VALUE = "m";
+ public static final String PARAM_INTERIOR_GAP = "g";
+ public static final String PARAM_LABELS = "l";
+ public static final String PARAM_VALUES = "v";
+
+ public String getKey() {
+ return "xradar";
+ }
+
+ @Override
+ protected Plot getPlot(ChartParameters params) {
+ SpiderWebPlot plot = new SpiderWebPlot(createDataset(params));
+ plot.setStartAngle(0D);
+ plot.setOutlineVisible(false);
+ plot.setAxisLinePaint(Color.decode("0xCCCCCC"));
+ plot.setSeriesOutlineStroke(new BasicStroke(2f));
+
+ if (params.getValue(PARAM_INTERIOR_GAP) != null) {
+ plot.setInteriorGap(Double.parseDouble(params.getValue(PARAM_INTERIOR_GAP, "0.4", false)));
+ }
+ if (params.getValue(PARAM_MAX_VALUE) != null) {
+ plot.setMaxValue(Double.parseDouble(params.getValue(PARAM_MAX_VALUE, "100", false)));
+ }
+ configureColors(plot, params);
+ return plot;
+ }
+
+ private void configureColors(SpiderWebPlot plot, ChartParameters params) {
+ String[] colors = params.getValues(PARAM_COLOR, "|");
+ for (int i = 0; i < colors.length; i++) {
+ plot.setSeriesPaint(i, Color.decode("0x" + colors[i]));
+ }
+ }
+
+ private CategoryDataset createDataset(ChartParameters params) {
+ String[] labels = params.getValues(PARAM_LABELS, ",");
+ String[] values = params.getValues(PARAM_VALUES, "|");
+
+ DefaultCategoryDataset set = new DefaultCategoryDataset();
+ for (int indexValues = 0; indexValues < values.length; indexValues++) {
+ String[] fields = StringUtils.split(values[indexValues], ",");
+ for (int i = 0; i < fields.length; i++) {
+ set.addValue(Double.parseDouble(fields[i]), "" + indexValues, labels[i]);
+ }
+ }
+ return set;
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/Clouds.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/Clouds.java
new file mode 100644
index 00000000000..f0b319604b2
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/Clouds.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.plugins.core.clouds;
+
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.GwtPage;
+import org.sonar.api.web.NavigationSection;
+import org.sonar.api.web.ResourceScope;
+import org.sonar.api.web.UserRole;
+import org.sonar.plugins.core.clouds.client.GwtClouds;
+
+@NavigationSection(NavigationSection.RESOURCE)
+@ResourceScope({Resource.SCOPE_SET, Resource.SCOPE_SPACE})
+@UserRole(UserRole.USER)
+public class Clouds extends GwtPage {
+
+ public String getGwtId() {
+ return GwtClouds.GWT_ID;
+ }
+
+ public String getTitle() {
+ return "Clouds";
+ }
+
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/Calculator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/Calculator.java
new file mode 100644
index 00000000000..5202b4feebc
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/Calculator.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.plugins.core.clouds.client;
+
+import org.sonar.plugins.core.clouds.client.model.Color;
+
+public class Calculator {
+
+ private Float minValue;
+ private Float maxValue;
+ private Float minPercent;
+ private Float maxPercent;
+
+ public Calculator(Float minPercent, Float maxPercent) {
+ this.minPercent = minPercent;
+ this.maxPercent = maxPercent;
+ }
+
+ public void updateMaxAndMin(Float value){
+ updateMaxValue(value);
+ updateMinValue(value);
+ }
+
+ public Integer getFontSizePercent(Integer value) {
+ float divisor = getMaxValue() - getMinValue();
+ float size = getMinPercent();
+ if (divisor != 0) {
+ float multiplier = (getMaxPercent() - getMinPercent()) / divisor;
+ size = getMinPercent() +
+ ((getMaxValue() - (getMaxValue() - (value - getMinValue()))) * multiplier);
+ }
+ return Float.valueOf(size).intValue();
+ }
+
+ public String getFontColor(float value) {
+ float interval = (getMaxPercent() - getMinPercent()) / 2f;
+ float mean = (getMinPercent() + getMaxPercent()) / 2f;
+
+ Color minColor = new Color(191/255f, 0f, 21/255f); // red
+ Color meanColor = new Color(77/255f, 5/255f, 177/255f); // purple
+ Color maxColor = new Color(23/255f, 96/255f, 191/255f); // blue
+
+ Color color;
+ if (value > mean) {
+ float valuePercent = ((value - mean) / interval) * 100f;
+ color = mixColorWith(maxColor, meanColor, valuePercent);
+ } else {
+ float valuePercent = ((mean - value) / interval) * 100f;
+ color = mixColorWith(minColor, meanColor, valuePercent);
+ }
+
+ int r = Float.valueOf(color.getRed()* 255f).intValue();
+ int g = Float.valueOf(color.getGreen() * 255f).intValue();
+ int b = Float.valueOf(color.getBlue() * 255f).intValue();
+
+ return ("rgb("+ r +","+ g +","+ b +")");
+ }
+
+ private Color mixColorWith(Color currentColor, Color mask, float value){
+ float opacity = value / 100f;
+
+ float r = (currentColor.getRed() * opacity) + (mask.getRed() * (1f - opacity));
+ float g = (currentColor.getGreen() * opacity) + (mask.getGreen() * (1f - opacity));
+ float b = (currentColor.getBlue() * opacity) + (mask.getBlue() * (1f - opacity));
+
+ return new Color(r, g, b);
+ }
+
+
+ private void updateMaxValue(Float value) {
+ if (maxValue == null) {
+ maxValue = value;
+ } else if (value > maxValue) {
+ maxValue = value;
+ }
+ }
+
+ private void updateMinValue(Float value) {
+ if (minValue == null) {
+ minValue = value;
+ } else if (value < minValue) {
+ minValue = value;
+ }
+ }
+
+
+ public Float getMinValue() {
+ return minValue;
+ }
+
+ public Float getMaxValue() {
+ return maxValue;
+ }
+
+ public Float getMinPercent() {
+ return minPercent;
+ }
+
+ public Float getMaxPercent() {
+ return maxPercent;
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/GwtClouds.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/GwtClouds.java
new file mode 100644
index 00000000000..4770f3d64ba
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/GwtClouds.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.plugins.core.clouds.client;
+
+import com.google.gwt.core.client.JavaScriptException;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.logical.shared.SelectionEvent;
+import com.google.gwt.event.logical.shared.SelectionHandler;
+import com.google.gwt.user.client.ui.*;
+import org.sonar.api.web.gwt.client.AbstractPage;
+import org.sonar.api.web.gwt.client.ResourceDictionary;
+import org.sonar.api.web.gwt.client.webservices.*;
+import org.sonar.api.web.gwt.client.webservices.WSMetrics.Metric;
+import org.sonar.api.web.gwt.client.webservices.WSMetrics.MetricsList;
+import org.sonar.api.web.gwt.client.widgets.LoadingLabel;
+import org.sonar.plugins.core.clouds.client.widget.ClassCloudsWidget;
+import org.sonar.plugins.core.clouds.client.widget.TabWidget;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class GwtClouds extends AbstractPage {
+
+ public static final String GWT_ID = "org.sonar.plugins.core.clouds.GwtClouds";
+
+ private Panel cloudsPanel;
+ private ListBox metricsListBox;
+ private Label sizeAndColorLabel;
+ private TabWidget sizeTabs;
+ private Resources resources;
+
+ private final List<SizeMetric> SIZE_METRICS = Arrays.asList(
+ new SizeMetric("Quick Wins", WSMetrics.NCLOC),
+ new SizeMetric("Top risk", WSMetrics.FUNCTION_COMPLEXITY));
+
+ private final List<Metric> COLOR_METRICS = Arrays.asList(WSMetrics.COVERAGE, WSMetrics.VIOLATIONS_DENSITY);
+
+ public void onModuleLoad() {
+ cloudsPanel = new FlowPanel();
+ displayView(cloudsPanel);
+ loadClouds();
+ }
+
+ protected void loadClouds() {
+ String projectKey = ResourceDictionary.getResourceKey();
+ final List<Metric> metricsToGet = new ArrayList<Metric>();
+ for (SizeMetric size : SIZE_METRICS) {
+ metricsToGet.add(size.getSizeMetric());
+ }
+ for (Metric color : COLOR_METRICS) {
+ metricsToGet.add(color);
+ }
+ if (projectKey != null) {
+ cloudsPanel.add(new LoadingLabel());
+
+ Query<Resources> resourcesQuery = ResourcesQuery.get(projectKey).setDepth(-1).setScopes(Resource.SCOPE_ENTITY).setMetrics(metricsToGet);
+ QueryCallBack<Resources> resourcesCb = new BaseQueryCallback<Resources>() {
+ public void onResponse(Resources response, JavaScriptObject jsonRawResponse) {
+ resources = response;
+ }
+ };
+ Query<MetricsList> metrics = MetricsQuery.get().setUserManaged(false);
+ QueryCallBack<MetricsList> metricsCb = new BaseQueryCallback<MetricsList>() {
+ public void onResponse(MetricsList response, JavaScriptObject jsonRawResponse) {
+ // nothing to do WSMetrics.getUpdateMetricsFromServer will update the metrics labels
+ }
+ };
+ metricsCb = WSMetrics.getUpdateMetricsFromServer(metricsCb);
+
+ QueryCallBack<VoidResponse> updateCloudsCb = new BaseQueryCallback<VoidResponse>() {
+ public void onResponse(VoidResponse response, JavaScriptObject jsonRawResponse) {
+ updateClouds(resources);
+ }
+ };
+
+ SequentialQueries.get().add(resourcesQuery, resourcesCb).add(metrics, metricsCb).execute(updateCloudsCb);
+ }
+ }
+
+ private void updateClouds(Resources resources) {
+ cloudsPanel.clear();
+ Panel metricSelectPanel = getMetricColorSelectBox(resources);
+ sizeTabs = new TabWidget(new SelectionHandler<Integer>() {
+ public void onSelection(SelectionEvent<Integer> event) {
+ renderClassCloudsForCurrentMetric();
+ }
+ });
+ for (SizeMetric size : SIZE_METRICS) {
+ ClassCloudsWidget classCloudsTab = new ClassCloudsWidget(resources.getResources(), size.getSizeMetric());
+ sizeTabs.addTab(classCloudsTab, size.getTabName(), size.getTabNameId());
+ }
+
+ cloudsPanel.add(metricSelectPanel);
+ cloudsPanel.add(sizeTabs);
+ }
+
+ private Panel getMetricColorSelectBox(Resources resources) {
+ HTMLPanel metricSelectPanel = new HTMLPanel("<div id='select_metric' class='metricSelectBox small'> </div>");
+ sizeAndColorLabel = new InlineLabel();
+ sizeAndColorLabel.setStyleName("labelText gray");
+ metricSelectPanel.add(sizeAndColorLabel, "select_metric");
+ metricsListBox = new ListBox(false);
+ for (Metric color : COLOR_METRICS) {
+ if (resources.onceContainsMeasure(color)) {
+ metricsListBox.addItem(color.getName(), color.getKey());
+ }
+ }
+ metricSelectPanel.add(metricsListBox, "select_metric");
+
+ metricsListBox.addChangeHandler(new ChangeHandler() {
+ public void onChange(ChangeEvent event) {
+ renderClassCloudsForCurrentMetric();
+ }
+ });
+ return metricSelectPanel;
+ }
+
+ private void generateSizeAndColorLabel() {
+ sizeAndColorLabel.setText("Size : " + getCurrentSizeMetric().getName() + ", color : ");
+ }
+
+ private void renderClassCloudsForCurrentMetric() {
+ Widget widget = sizeTabs.getSelectedWidget();
+ if (widget instanceof ClassCloudsWidget) {
+ Metric current = getCurrentColorMetric();
+ ClassCloudsWidget classCloudsWidget = (ClassCloudsWidget) widget;
+ classCloudsWidget.generateCloud(current);
+ generateSizeAndColorLabel();
+ }
+ }
+
+ private Metric getCurrentColorMetric() {
+ String metricKey = metricsListBox.getValue(metricsListBox.getSelectedIndex());
+ for (Metric color : COLOR_METRICS) {
+ if (color.getKey().equals(metricKey)) {
+ return color;
+ }
+ }
+ throw new JavaScriptException("Unable to find metric " + metricKey);
+ }
+
+ private Metric getCurrentSizeMetric() {
+ String selectedTabId = sizeTabs.getSelectedTabId();
+ for (SizeMetric size : SIZE_METRICS) {
+ if (size.getTabNameId().equals(selectedTabId)) {
+ return size.sizeMetric;
+ }
+ }
+ throw new JavaScriptException("Unable to find metric for tab " + selectedTabId);
+ }
+
+
+ private class SizeMetric {
+
+ private String tabName;
+ private Metric sizeMetric;
+
+ public SizeMetric(String tabName, Metric sizeMetric) {
+ super();
+ this.tabName = tabName;
+ this.sizeMetric = sizeMetric;
+ }
+
+ public String getTabName() {
+ return tabName;
+ }
+
+ public Metric getSizeMetric() {
+ return sizeMetric;
+ }
+
+ public String getTabNameId() {
+ return tabName.toLowerCase().replace(' ', '_');
+ }
+ }
+
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/model/CloudElement.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/model/CloudElement.java
new file mode 100644
index 00000000000..60f928d8f3e
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/model/CloudElement.java
@@ -0,0 +1,53 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.clouds.client.model;
+
+import org.sonar.api.web.gwt.client.webservices.Resource;
+
+
+public class CloudElement implements Comparable<CloudElement> {
+
+ private Integer fontSize;
+ private Float fontColor;
+ private Resource resource;
+
+ public CloudElement(Resource resource, Integer fontSize, Float fontColor) {
+ this.resource = resource;
+ this.fontSize = fontSize;
+ this.fontColor = fontColor;
+ }
+
+ public Resource getResource() {
+ return resource;
+ }
+
+ public Integer getFontSize() {
+ return fontSize;
+ }
+
+ public Float getFontColor() {
+ return fontColor;
+ }
+
+ public int compareTo(CloudElement cloudElement) {
+ return resource.getName().compareTo(cloudElement.getResource().getName());
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/model/Color.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/model/Color.java
new file mode 100644
index 00000000000..598c470f207
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/model/Color.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.plugins.core.clouds.client.model;
+
+public class Color {
+
+ private float red;
+ private float green;
+ private float blue;
+
+ public Color(float red, float green, float blue) {
+ this.red = red;
+ this.green = green;
+ this.blue = blue;
+ }
+
+ public float getRed() {
+ return red;
+ }
+
+ public float getGreen() {
+ return green;
+ }
+
+ public float getBlue() {
+ return blue;
+ }
+
+ @Override
+ public String toString() {
+ return ("red : "+ red + ", green : "+ green + ", blue : "+ blue );
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/widget/ClassCloudsWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/widget/ClassCloudsWidget.java
new file mode 100644
index 00000000000..49c28d87656
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/widget/ClassCloudsWidget.java
@@ -0,0 +1,146 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.clouds.client.widget;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.*;
+import org.sonar.api.web.gwt.client.Utils;
+import org.sonar.api.web.gwt.client.webservices.Measure;
+import org.sonar.api.web.gwt.client.webservices.Resource;
+import org.sonar.api.web.gwt.client.webservices.WSMetrics.Metric;
+import org.sonar.api.web.gwt.client.widgets.LoadingLabel;
+import org.sonar.plugins.core.clouds.client.Calculator;
+import org.sonar.plugins.core.clouds.client.GwtClouds;
+import org.sonar.plugins.core.clouds.client.model.CloudElement;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class ClassCloudsWidget extends Composite {
+
+ private Panel main;
+ private Metric sizeMetric;
+ private List<Resource> resources;
+ private float minSizePercent = 60f;
+ private float maxSizePercent = 240f;
+
+ private Calculator sizeCalculator = new Calculator(minSizePercent, maxSizePercent);
+ private Calculator colorCalculator = new Calculator(0f, 100f);
+
+ public ClassCloudsWidget(List<Resource> resources, Metric sizeMetric) {
+ this.sizeMetric = sizeMetric;
+ this.main = new FlowPanel();
+ this.resources = resources;
+ initWidget(main);
+ }
+
+ public Metric getSizeMetric() {
+ return sizeMetric;
+ }
+
+ public void generateCloud(Metric colorMetric) {
+ main.clear();
+ LoadingLabel loading = new LoadingLabel();
+ main.add(loading);
+ if (colorMetric.equals(colorMetric)) {
+ List<CloudElement> cloudElements = getCloudElements(resources, colorMetric);
+ createClouds(cloudElements, colorMetric);
+ }
+ main.remove(loading);
+ }
+
+ private List<CloudElement> getCloudElements(List<Resource> resources, Metric colorMetric) {
+ List<CloudElement> tagList = new ArrayList<CloudElement>();
+ for (Resource resource : resources) {
+ Measure sizeMeasure = getMeasure(resource, sizeMetric);
+ Measure colorMeasure = getMeasure(resource, colorMetric);
+
+ if (sizeMeasure != null && colorMeasure != null) {
+ Integer size = getMeasureValue(sizeMeasure.getValue());
+ float color = colorMeasure.getValue().floatValue();
+ tagList.add(new CloudElement(resource, size, color));
+ sizeCalculator.updateMaxAndMin(Float.valueOf(size.toString()));
+ }
+ }
+ Collections.sort(tagList);
+ return tagList;
+ }
+
+ private Integer getMeasureValue(Double value) {
+ Float floatValue = (value.floatValue() * 100.0f);
+ return floatValue.intValue();
+ }
+
+ private Measure getMeasure(Resource project, Metric metricToFind) {
+ return project.getMeasure(metricToFind);
+ }
+
+ private void createClouds(List<CloudElement> cloudElements, Metric colorMetric) {
+ for (CloudElement tag : cloudElements) {
+ HTML className = new HTML(
+ "<span style=\"font-size:" + Integer.toString(sizeCalculator.getFontSizePercent(tag.getFontSize())) +
+ "%; color:" + colorCalculator.getFontColor(tag.getFontColor()) + "\" >" +
+ tag.getResource().getName() + "</span>\n");
+ className.setStyleName("inline");
+
+ Hyperlink link = createLink(tag, colorMetric);
+ link.setHTML(className.getHTML());
+ main.add(link);
+ }
+ }
+
+ private Hyperlink createLink(CloudElement tag, final Metric colorMetric) {
+ Hyperlink link = new Hyperlink();
+ link.setStyleName("tag inline");
+ String tooltip = getTooltip(tag.getResource(), colorMetric);
+ link.getElement().setAttribute("title", tooltip);
+ link.getElement().setAttribute("rel", tooltip);
+
+ String sizeCss = Float.toString(maxSizePercent / 100f) + "em";
+ link.setHeight(sizeCss);
+ final Resource clickResource = tag.getResource();
+ link.addClickHandler(new ClickHandler() {
+ public void onClick(final ClickEvent event) {
+ if (clickResource.getCopy() != null) {
+ Window.Location.assign(Utils.getServerUrl() + "/plugins/resource/" + clickResource.getCopy() + "?page=" + GwtClouds.GWT_ID);
+ } else {
+ Utils.openResourcePopup(clickResource, colorMetric.getKey());
+ }
+ }
+ });
+
+ return link;
+ }
+
+ private String getTooltip(Resource resource, Metric colorMetric) {
+ Measure sizeMeasure = getMeasure(resource, sizeMetric);
+ String sizeMetricName = sizeMetric.getName();
+ String sizeMetricValue = sizeMeasure.getFormattedValue();
+
+ Measure colorMeasure = getMeasure(resource, colorMetric);
+ String colorMetricName = colorMetric.getName();
+ String colorMetricValue = colorMeasure.getFormattedValue();
+
+ return resource.getName(true) + ", " + sizeMetricName + " : " + sizeMetricValue + ", " + colorMetricName + " : " + colorMetricValue;
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/widget/TabWidget.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/widget/TabWidget.java
new file mode 100644
index 00000000000..7e9b571a4e0
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/clouds/client/widget/TabWidget.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.plugins.core.clouds.client.widget;
+
+import com.google.gwt.event.logical.shared.SelectionEvent;
+import com.google.gwt.event.logical.shared.SelectionHandler;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwt.user.client.ui.TabPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+public class TabWidget extends Composite {
+
+ private TabPanel tab = new TabPanel();
+
+ private Integer nbTab;
+ private final Integer defaultSelectedTabPosition = 0;
+ private String selectedTabId;
+ private int selectedIndex;
+
+ public TabWidget(final SelectionHandler<Integer> selectionListener) {
+ nbTab = 0;
+ initWidget(tab);
+ tab.setWidth("100%");
+
+ tab.addSelectionHandler(new SelectionHandler<Integer>() {
+ public void onSelection(SelectionEvent<Integer> event) {
+ selectedTabId = tab.getWidget(event.getSelectedItem()).getElement().getId().replace("_tab_content", "");
+ selectedIndex = event.getSelectedItem();
+ selectionListener.onSelection(event);
+ }
+ });
+
+ }
+
+ public String getSelectedTabId() {
+ return selectedTabId;
+ }
+
+ public Widget getSelectedWidget() {
+ return tab.getWidget(selectedIndex);
+ }
+
+ public void addTab(Widget widget, String tabName, String id) {
+ widget.getElement().setId(id + "_tab_content");
+ tab.add(widget, createTabLabel(tabName, id));
+ if (nbTab.equals(defaultSelectedTabPosition)) {
+ tab.selectTab(defaultSelectedTabPosition);
+ }
+ nbTab++;
+ }
+
+ private Label createTabLabel(String tabName, String id) {
+ Label tabLabel = new Label(tabName);
+ tabLabel.getElement().setId(id + "_tab_title");
+ tabLabel.addStyleName("tab_title");
+ return tabLabel;
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/colorizers/JavaColorizerFormat.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/colorizers/JavaColorizerFormat.java
new file mode 100644
index 00000000000..45d6dc5d754
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/colorizers/JavaColorizerFormat.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.plugins.core.colorizers;
+
+import java.util.List;
+
+import org.sonar.api.resources.Java;
+import org.sonar.api.web.CodeColorizerFormat;
+import org.sonar.colorizer.CodeColorizer;
+import org.sonar.colorizer.Tokenizer;
+
+public class JavaColorizerFormat extends CodeColorizerFormat{
+
+ public JavaColorizerFormat() {
+ super(Java.KEY);
+ }
+
+ @Override
+ public List<Tokenizer> getTokenizers() {
+ return CodeColorizer.Format.JAVA.getTokenizers();
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/coverageviewer/CoverageViewerDefinition.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/coverageviewer/CoverageViewerDefinition.java
new file mode 100644
index 00000000000..ba9231e491d
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/coverageviewer/CoverageViewerDefinition.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.plugins.core.coverageviewer;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.*;
+
+@ResourceQualifier(Resource.QUALIFIER_CLASS)
+@NavigationSection(NavigationSection.RESOURCE_TAB)
+@DefaultTab(metrics={CoreMetrics.COVERAGE_KEY, CoreMetrics.LINES_TO_COVER_KEY, CoreMetrics.UNCOVERED_LINES_KEY, CoreMetrics.LINE_COVERAGE_KEY, CoreMetrics.CONDITIONS_TO_COVER_KEY, CoreMetrics.UNCOVERED_CONDITIONS_KEY, CoreMetrics.BRANCH_COVERAGE_KEY})
+@UserRole(UserRole.CODEVIEWER)
+public class CoverageViewerDefinition extends GwtPage {
+
+ public String getTitle() {
+ return "Coverage";
+ }
+
+ public String getGwtId() {
+ return "org.sonar.plugins.core.coverageviewer.CoverageViewer";
+ }
+}
+
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/coverageviewer/client/CoveragePanel.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/coverageviewer/client/CoveragePanel.java
new file mode 100644
index 00000000000..fabda63343e
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/coverageviewer/client/CoveragePanel.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.plugins.core.coverageviewer.client;
+
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.SourcePanel;
+import org.sonar.wsclient.gwt.AbstractCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class CoveragePanel extends SourcePanel {
+
+ private Map<Integer, String> hitsByLine = new HashMap<Integer, String>();
+ private Map<Integer, String> branchHitsByLine = new HashMap<Integer, String>();
+
+
+ public CoveragePanel(Resource resource) {
+ super(resource);
+ loadCoverageHits(resource);
+ }
+
+ private void loadCoverageHits(Resource resource) {
+ ResourceQuery query = ResourceQuery.createForResource(resource, Metrics.COVERAGE_LINE_HITS_DATA, Metrics.BRANCH_COVERAGE_HITS_DATA);
+ Sonar.getInstance().find(query, new AbstractCallback<Resource>() {
+
+ @Override
+ protected void doOnResponse(Resource resource) {
+ handleResponse(resource, Metrics.COVERAGE_LINE_HITS_DATA, hitsByLine);
+ handleResponse(resource, Metrics.BRANCH_COVERAGE_HITS_DATA, branchHitsByLine);
+ setStarted();
+ }
+ });
+ }
+
+ private void handleResponse(Resource resource, String metric, Map<Integer, String> values) {
+ if (resource==null || resource.getMeasure(metric)==null) {
+ return;
+ }
+
+ values.clear();
+ String linesValue = resource.getMeasure(metric).getData();
+ String[] lineWithValueArray;
+ if (linesValue.contains(",")) {
+ // deprecated - format before 1.9
+ lineWithValueArray = linesValue.split(",");
+ } else {
+ lineWithValueArray = linesValue.split(";");
+ }
+ for (String lineWithValue : lineWithValueArray) {
+ String[] elt = lineWithValue.split("=");
+ if (elt != null && elt.length == 2) {
+ values.put(Integer.parseInt(elt[0]), elt[1]);
+ }
+ }
+ }
+
+
+ @Override
+ protected boolean shouldDecorateLine(int index) {
+ return index > 0;
+ }
+
+ @Override
+ protected List<Row> decorateLine(int index, String source) {
+ Row row = new Row().setLineIndex(index, "");
+
+ String hits = hitsByLine.get(index);
+ String branchHits = branchHitsByLine.get(index);
+ boolean hasLineCoverage = (null != hits);
+ boolean hasBranchCoverage = (null != branchHits);
+ boolean lineIsCovered = (hasLineCoverage && Integer.parseInt(hits) > 0);
+ boolean branchIsCovered = (hasBranchCoverage && "100%".equals(branchHits));
+
+ row.setSource(source, "");
+ row.setValue("&nbsp;", "");
+ row.setValue2("&nbsp;", "");
+
+ if (lineIsCovered) {
+ if (branchIsCovered) {
+ row.setValue(hits, "green");
+ row.setValue2(branchHits, "green");
+ } else if (hasBranchCoverage) {
+ row.setValue(hits, "orange");
+ row.setValue2(branchHits, "orange");
+ row.setSource(source, "orange");
+ } else {
+ row.setValue(hits, "green");
+ }
+ } else if (hasLineCoverage) {
+ row.setValue(hits, "red");
+ row.setSource(source, "red");
+ if (hasBranchCoverage) {
+ row.setValue2(branchHits, "red");
+ } else {
+ row.setValue2("&nbsp;", "red");
+ }
+ }
+ return Arrays.asList(row);
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/coverageviewer/client/CoverageViewer.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/coverageviewer/client/CoverageViewer.java
new file mode 100644
index 00000000000..64752ddd7c0
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/coverageviewer/client/CoverageViewer.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.plugins.core.coverageviewer.client;
+
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.Page;
+import org.sonar.gwt.ui.ViewerHeader;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+
+public class CoverageViewer extends Page {
+ @Override
+ protected Widget doOnResourceLoad(Resource resource) {
+ FlowPanel panel = new FlowPanel();
+ panel.setWidth("100%");
+ panel.add(new CoverageHeader(resource));
+ panel.add(new CoveragePanel(resource));
+ return panel;
+ }
+
+ private static class CoverageHeader extends ViewerHeader {
+ public CoverageHeader(Resource resource) {
+ super(resource, new String[]{Metrics.COVERAGE, Metrics.LINE_COVERAGE, Metrics.UNCOVERED_LINES, Metrics.BRANCH_COVERAGE, Metrics.UNCOVERED_CONDITIONS});
+ }
+
+ @Override
+ protected void display(FlowPanel header, Resource resource) {
+ HorizontalPanel panel = new HorizontalPanel();
+ header.add(panel);
+
+ Measure measure = resource.getMeasure(Metrics.COVERAGE);
+ if (measure == null) {
+ addBigCell(panel, "-");
+ } else {
+ addBigCell(panel, measure.getFormattedValue());
+ }
+
+ addCell(panel, resource.getMeasure(Metrics.LINE_COVERAGE));
+ addCell(panel, resource.getMeasure(Metrics.UNCOVERED_LINES));
+ addCell(panel, resource.getMeasure(Metrics.BRANCH_COVERAGE));
+ addCell(panel, resource.getMeasure(Metrics.UNCOVERED_CONDITIONS));
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/defaultsourceviewer/DefaultSourceViewer.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/defaultsourceviewer/DefaultSourceViewer.java
new file mode 100644
index 00000000000..f400727cc55
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/defaultsourceviewer/DefaultSourceViewer.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.plugins.core.defaultsourceviewer;
+
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.*;
+import org.sonar.plugins.core.defaultsourceviewer.client.GwtDefaultSourceViewer;
+
+@ResourceScope(Resource.SCOPE_ENTITY)
+@NavigationSection(NavigationSection.RESOURCE_TAB)
+@DefaultTab
+@UserRole(UserRole.CODEVIEWER)
+public class DefaultSourceViewer extends GwtPage {
+ public String getTitle() {
+ return "Sources";
+ }
+
+ public String getGwtId() {
+ return GwtDefaultSourceViewer.GWT_ID;
+ }
+
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/defaultsourceviewer/client/GwtDefaultSourceViewer.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/defaultsourceviewer/client/GwtDefaultSourceViewer.java
new file mode 100644
index 00000000000..fd9a929ae58
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/defaultsourceviewer/client/GwtDefaultSourceViewer.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.plugins.core.defaultsourceviewer.client;
+
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.DefaultSourcePanel;
+import org.sonar.gwt.ui.Page;
+import org.sonar.gwt.ui.ViewerHeader;
+import org.sonar.wsclient.services.Resource;
+
+public class GwtDefaultSourceViewer extends Page {
+
+ public static final String GWT_ID = "org.sonar.plugins.core.defaultsourceviewer.GwtDefaultSourceViewer";
+
+ @Override
+ protected Widget doOnResourceLoad(Resource resource) {
+ FlowPanel panel = new FlowPanel();
+ panel.setWidth("100%");
+ panel.add(new SimpleHeader(resource));
+ panel.add(new DefaultSourcePanel(resource));
+ return panel;
+ }
+
+ private static class SimpleHeader extends ViewerHeader {
+ public SimpleHeader(Resource resource) {
+ super(resource, new String[]{
+ Metrics.LINES,
+ Metrics.NCLOC,
+ Metrics.FUNCTIONS,
+ Metrics.ACCESSORS,
+ Metrics.PARAGRAPHS,
+
+ Metrics.STATEMENTS,
+ Metrics.COMPLEXITY,
+ Metrics.FUNCTION_COMPLEXITY,
+ Metrics.PARAGRAPH_COMPLEXITY,
+
+ Metrics.COMMENT_LINES_DENSITY,
+ Metrics.COMMENT_LINES,
+ Metrics.COMMENTED_OUT_CODE_LINES,
+ Metrics.COMMENT_BLANK_LINES,
+
+ Metrics.PUBLIC_DOCUMENTED_API_DENSITY,
+ Metrics.PUBLIC_UNDOCUMENTED_API,
+ Metrics.PUBLIC_API,
+
+ Metrics.CLASSES,
+ Metrics.NUMBER_OF_CHILDREN,
+ Metrics.DEPTH_IN_TREE,
+ Metrics.RFC
+ }
+ );
+ }
+
+ @Override
+ protected void display(FlowPanel header, Resource resource) {
+ HorizontalPanel panel = new HorizontalPanel();
+ addCell(panel,
+ resource.getMeasure(Metrics.LINES),
+ resource.getMeasure(Metrics.NCLOC),
+ resource.getMeasure(Metrics.FUNCTIONS),
+ resource.getMeasure(Metrics.ACCESSORS),
+ resource.getMeasure(Metrics.PARAGRAPHS));
+
+ addCell(panel,
+ resource.getMeasure(Metrics.STATEMENTS),
+ resource.getMeasure(Metrics.COMPLEXITY),
+ resource.getMeasure(Metrics.FUNCTION_COMPLEXITY),
+ resource.getMeasure(Metrics.PARAGRAPH_COMPLEXITY));
+
+ addCell(panel,
+ resource.getMeasure(Metrics.COMMENT_LINES_DENSITY),
+ resource.getMeasure(Metrics.COMMENT_LINES),
+ resource.getMeasure(Metrics.COMMENTED_OUT_CODE_LINES),
+ resource.getMeasure(Metrics.COMMENT_BLANK_LINES));
+
+ addCell(panel,
+ resource.getMeasure(Metrics.PUBLIC_DOCUMENTED_API_DENSITY),
+ resource.getMeasure(Metrics.PUBLIC_UNDOCUMENTED_API),
+ resource.getMeasure(Metrics.PUBLIC_API));
+
+ addCell(panel,
+ resource.getMeasure(Metrics.CLASSES),
+ resource.getMeasure(Metrics.NUMBER_OF_CHILDREN),
+ resource.getMeasure(Metrics.DEPTH_IN_TREE),
+ resource.getMeasure(Metrics.RFC));
+
+ if (panel.getWidgetCount() > 0) {
+ header.add(panel);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/duplicationsviewer/DuplicationsViewerDefinition.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/duplicationsviewer/DuplicationsViewerDefinition.java
new file mode 100644
index 00000000000..b679a144f76
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/duplicationsviewer/DuplicationsViewerDefinition.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.plugins.core.duplicationsviewer;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.*;
+import org.sonar.plugins.core.duplicationsviewer.client.DuplicationsViewer;
+
+@ResourceQualifier({Resource.QUALIFIER_CLASS,Resource.QUALIFIER_FILE})
+@NavigationSection(NavigationSection.RESOURCE_TAB)
+@DefaultTab(metrics={CoreMetrics.DUPLICATED_LINES_KEY, CoreMetrics.DUPLICATED_BLOCKS_KEY, CoreMetrics.DUPLICATED_FILES_KEY, CoreMetrics.DUPLICATED_LINES_DENSITY_KEY})
+@UserRole(UserRole.CODEVIEWER)
+public class DuplicationsViewerDefinition extends GwtPage {
+
+ public String getTitle() {
+ return "Duplications";
+ }
+
+ public String getGwtId() {
+ return DuplicationsViewer.GWT_ID;
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsPanel.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsPanel.java
new file mode 100644
index 00000000000..17f34ffa94b
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsPanel.java
@@ -0,0 +1,161 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.duplicationsviewer.client;
+
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.DefaultSourcePanel;
+import org.sonar.gwt.ui.ExpandCollapseLink;
+import org.sonar.gwt.ui.Loading;
+import org.sonar.gwt.ui.SourcePanel;
+
+import com.google.gwt.gen2.table.override.client.FlexTable;
+import com.google.gwt.gen2.table.override.client.FlexTable.FlexCellFormatter;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.xml.client.Document;
+import com.google.gwt.xml.client.Element;
+import com.google.gwt.xml.client.NodeList;
+import com.google.gwt.xml.client.XMLParser;
+import org.sonar.wsclient.gwt.AbstractCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+public class DuplicationsPanel extends Composite {
+
+ private final Panel panel;
+ private Loading loading;
+
+ public DuplicationsPanel(Resource resource) {
+ panel = new VerticalPanel();
+ loading = new Loading();
+ panel.add(loading);
+ initWidget(panel);
+ setStyleName("gwt-DuplicationsPanel");
+ getElement().setId("gwt-DuplicationsPanel");
+
+ loadDuplications(resource);
+ }
+
+ public void loadDuplications(Resource resource) {
+ ResourceQuery query = ResourceQuery.createForResource(resource, Metrics.DUPLICATIONS_DATA);
+ Sonar.getInstance().find(query, new DuplicationCallback());
+ }
+
+ private class DuplicationCallback extends AbstractCallback<Resource> {
+
+ public DuplicationCallback() {
+ super(loading);
+ }
+
+ @Override
+ protected void doOnResponse(Resource resource) {
+ loading.removeFromParent();
+ String duplications = null;
+ if (resource != null) {
+ Measure data = resource.getMeasure(Metrics.DUPLICATIONS_DATA);
+ if (data != null) {
+ duplications = data.getData();
+ }
+ }
+ if (duplications != null) {
+ processDuplications(duplications, resource);
+ }
+ }
+
+ private void processDuplications(String duplicationXMLData, Resource resource) {
+ Document parsed = XMLParser.parse(duplicationXMLData);
+ NodeList duplicationsXML = parsed.getElementsByTagName("duplication");
+
+ FlexTable table = getDuplicationsTable();
+
+ panel.add(table);
+ int rowCounter = 1;
+ for (int i = 0; i < duplicationsXML.getLength(); i++) {
+ Element duplicationXML = (Element) duplicationsXML.item(i);
+ String lines = duplicationXML.getAttribute("lines");
+ String startLine = duplicationXML.getAttribute("start");
+ String targetStartLine = duplicationXML.getAttribute("target-start");
+ String targetResourceKey = duplicationXML.getAttribute("target-resource");
+ renderDuplication(rowCounter, i, table, lines, startLine, targetStartLine, targetResourceKey, resource);
+ rowCounter+=2;
+ }
+ }
+
+ private FlexTable getDuplicationsTable() {
+ FlexTable table = new FlexTable();
+ table.setStylePrimaryName("duplicationsTable");
+ table.setText(0, 0, "");
+ table.setText(0, 1, "Nb lines");
+ table.setText(0, 2, "From line");
+ table.setText(0, 3, "File");
+ table.setText(0, 4, "From line");
+
+ table.getCellFormatter().getElement(0, 0).setId("expandCollapseCol");
+ table.getCellFormatter().getElement(0, 1).setId("nbLineCol");
+ table.getCellFormatter().getElement(0, 2).setId("lineFromCol");
+ table.getCellFormatter().getElement(0, 3).setId("fileCol");
+
+ setRowStyle(0, table, "header", false);
+ return table;
+ }
+
+ private void renderDuplication(int row, int duplicationCounter, FlexTable table, String lines, String startLine, String targetStartLine, String targetResourceKey, final Resource resource) {
+ String style = (duplicationCounter % 2 == 0) ? "odd" : "even";
+
+ SourcePanel src = new DefaultSourcePanel(resource, new Integer(startLine), new Integer(lines));
+ src.getElement().setId("source-panel-" + targetResourceKey.replace('.', '_'));
+ src.setVisible(false);
+
+ ExpandCollapseLink link = new ExpandCollapseLink(src);
+
+ table.setWidget(row, 0, link);
+ table.setText(row, 1, lines);
+ table.setText(row, 2, startLine);
+ if (targetResourceKey.equals(resource.getKey())) {
+ targetResourceKey = "Same file";
+ }
+ if (targetResourceKey.contains(":")) {
+ targetResourceKey = targetResourceKey.substring(targetResourceKey.lastIndexOf(':') + 1);
+ }
+ table.setText(row, 3, targetResourceKey);
+ table.setText(row, 4, targetStartLine);
+ setRowStyle(row, table, style, false);
+
+ FlexCellFormatter frmt = (FlexCellFormatter)table.getCellFormatter();
+ frmt.setColSpan(row + 1, 1, 4);
+ table.setWidget(row + 1, 1, src);
+ setRowStyle(row + 1, table, style, true);
+
+ }
+
+ private void setRowStyle(int row, FlexTable table, String style, boolean isPanelRow) {
+ table.getCellFormatter().setStyleName(row, 0, style);
+ table.getCellFormatter().setStyleName(row, 1, style);
+ if (!isPanelRow) {
+ table.getCellFormatter().setStyleName(row, 2, style);
+ table.getCellFormatter().setStyleName(row, 3, style);
+ table.getCellFormatter().setStyleName(row, 4, style);
+ }
+ }
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsViewer.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsViewer.java
new file mode 100644
index 00000000000..742e0975dc7
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsViewer.java
@@ -0,0 +1,79 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.duplicationsviewer.client;
+
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Panel;
+import com.google.gwt.user.client.ui.Widget;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.Page;
+import org.sonar.gwt.ui.ViewerHeader;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+
+public class DuplicationsViewer extends Page {
+
+ public static final String GWT_ID = "org.sonar.plugins.core.duplicationsviewer.DuplicationsViewer";
+
+ @Override
+ protected Widget doOnResourceLoad(Resource resource) {
+ FlowPanel panel = new FlowPanel();
+ panel.setWidth("100%");
+ panel.add(new DuplicationsHeader(resource));
+ panel.add(new DuplicationsPanel(resource));
+ return panel;
+ }
+
+ private static class DuplicationsHeader extends ViewerHeader {
+ public DuplicationsHeader(Resource resource) {
+ super(resource, new String[]{Metrics.DUPLICATED_LINES_DENSITY, Metrics.LINES, Metrics.DUPLICATED_LINES, Metrics.DUPLICATED_BLOCKS});
+ }
+
+ @Override
+ protected void display(FlowPanel header, Resource resource) {
+ Panel panel = new HorizontalPanel();
+ header.add(panel);
+
+ Measure measure = resource.getMeasure(Metrics.DUPLICATED_LINES_DENSITY);
+ if (measure == null) {
+ addBigCell(panel, "0");
+ } else {
+ addBigCell(panel, measure.getFormattedValue());
+ }
+
+ addCell(panel, getDefaultMeasure(resource, Metrics.LINES, "lines"));
+ addCell(panel, getDefaultMeasure(resource, Metrics.DUPLICATED_LINES, "Duplicated lines"));
+ addCell(panel, getDefaultMeasure(resource, Metrics.DUPLICATED_BLOCKS, "Duplicated blocks"));
+ }
+
+ private Measure getDefaultMeasure(Resource resource, String metric, String label) {
+ Measure measure = resource.getMeasure(metric);
+ if (measure == null || measure.getValue() == null) {
+ measure = new Measure();
+ measure.setMetricName(label);
+ measure.setValue(0.0);
+ measure.setFormattedValue("0");
+ }
+ return measure;
+ }
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/Hotspots.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/Hotspots.java
new file mode 100644
index 00000000000..35ec96f4e93
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/Hotspots.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.plugins.core.hotspots;
+
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.GwtPage;
+import org.sonar.api.web.NavigationSection;
+import org.sonar.api.web.ResourceScope;
+import org.sonar.api.web.UserRole;
+
+@NavigationSection(NavigationSection.RESOURCE)
+@ResourceScope({Resource.SCOPE_SET, Resource.SCOPE_SPACE})
+@UserRole(UserRole.USER)
+public class Hotspots extends GwtPage {
+
+ public String getTitle() {
+ return "Hotspots";
+ }
+
+ public String getGwtId() {
+ return "org.sonar.plugins.core.hotspots.GwtHotspots";
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/GwtHotspots.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/GwtHotspots.java
new file mode 100644
index 00000000000..5f9f3edae0c
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/GwtHotspots.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.plugins.core.hotspots.client;
+
+import com.google.gwt.gen2.table.override.client.Grid;
+import com.google.gwt.user.client.ui.VerticalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.Page;
+import org.sonar.plugins.core.hotspots.client.widget.MetricHotspot;
+import org.sonar.plugins.core.hotspots.client.widget.MostBadlyDesignedFiles;
+import org.sonar.plugins.core.hotspots.client.widget.MostViolatedResources;
+import org.sonar.plugins.core.hotspots.client.widget.MostViolatedRules;
+import org.sonar.wsclient.services.Resource;
+
+public class GwtHotspots extends Page {
+ @Override
+ protected Widget doOnResourceLoad(Resource resource) {
+ Grid grid = new Grid(1, 2);
+ grid.setStylePrimaryName("gwt-Hotspots");
+ loadHotspots(grid, resource);
+ return grid;
+ }
+
+
+ private void loadHotspots(Grid grid, Resource resource) {
+ VerticalPanel column1 = new VerticalPanel();
+ column1.setStyleName("hotspotcol");
+ VerticalPanel column2 = new VerticalPanel();
+ column2.setStyleName("hotspotcol");
+
+ column1.add(new MostViolatedRules(resource));
+ column1.add(new MetricHotspot(resource, Metrics.TEST_EXECUTION_TIME, I18nConstants.INSTANCE.titleLongestTests()));
+ column1.add(new MetricHotspot(resource, Metrics.COMPLEXITY, I18nConstants.INSTANCE.titleMostComplexResources()));
+ column1.add(new MetricHotspot(resource, Metrics.DUPLICATED_LINES, I18nConstants.INSTANCE.titleMostDuplicatedResources()));
+ column1.add(new MostBadlyDesignedFiles(resource));
+
+ column2.add(new MostViolatedResources(resource));
+ column2.add(new MetricHotspot(resource, Metrics.UNCOVERED_LINES, I18nConstants.INSTANCE.titleLessTested()));
+ column2.add(new MetricHotspot(resource, Metrics.FUNCTION_COMPLEXITY, I18nConstants.INSTANCE.titleMostComplexMethods()));
+ column2.add(new MetricHotspot(resource, Metrics.PUBLIC_UNDOCUMENTED_API, I18nConstants.INSTANCE.titleMostUndocumentedAPI()));
+
+ grid.setWidget(0, 0, column1);
+ grid.setWidget(0, 1, column2);
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/I18nConstants.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/I18nConstants.java
new file mode 100644
index 00000000000..799b8267175
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/I18nConstants.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.plugins.core.hotspots.client;
+
+import com.google.gwt.core.client.GWT;
+
+public interface I18nConstants extends com.google.gwt.i18n.client.Constants {
+
+ static I18nConstants INSTANCE = GWT.create(I18nConstants.class);
+
+ @DefaultStringValue("Most violated rules")
+ String titleMostViolatedRules();
+
+ @DefaultStringValue("Most violated")
+ String titleMostViolatedResources();
+
+ @DefaultStringValue("Longest unit tests")
+ String titleLongestTests();
+
+ @DefaultStringValue("Highest complexity")
+ String titleMostComplexResources();
+
+ @DefaultStringValue("Highest duplications")
+ String titleMostDuplicatedResources();
+
+ @DefaultStringValue("Highest untested lines")
+ String titleLessTested();
+
+ @DefaultStringValue("Highest average method complexity")
+ String titleMostComplexMethods();
+
+ @DefaultStringValue("Most undocumented APIs")
+ String titleMostUndocumentedAPI();
+
+ @DefaultStringValue("No measures")
+ String noMeasures();
+
+ @DefaultStringValue("Any priority")
+ String anyPriority();
+
+ @DefaultStringValue("more")
+ String moreDetails();
+
+ @DefaultStringValue("Lack of Cohesion of Methods")
+ String lcom4();
+
+ @DefaultStringValue("Response for class")
+ String rfc();
+
+ @DefaultStringValue("Highest")
+ String designTitle();
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/AbstractHotspot.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/AbstractHotspot.java
new file mode 100644
index 00000000000..f214e9f58d9
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/AbstractHotspot.java
@@ -0,0 +1,124 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.hotspots.client.widget;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.*;
+import org.sonar.gwt.Links;
+import org.sonar.gwt.ui.Loading;
+import org.sonar.plugins.core.hotspots.client.I18nConstants;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+
+public abstract class AbstractHotspot extends Composite {
+
+ private Panel hotspot;
+ private Panel data;
+ private Resource resource;
+
+ public static final int LIMIT = 5;
+
+ protected AbstractHotspot(String id, Resource resource) {
+ this.resource = resource;
+ hotspot = new VerticalPanel();
+ hotspot.getElement().setId(id);
+ hotspot.setStyleName("gwt-HotspotPanel");
+ initWidget(hotspot);
+ }
+
+ public Resource getResource() {
+ return resource;
+ }
+
+ @Override
+ public void onLoad() {
+ hotspot.add(createHeader());
+ data = new SimplePanel();
+ hotspot.add(data);
+ loadData();
+ }
+
+ protected void loadData() {
+ data.clear();
+ data.add(new Loading());
+ doLoadData();
+ }
+
+ abstract Widget createHeader();
+
+ abstract void doLoadData();
+
+ protected void render(Widget widget) {
+ data.clear();
+ data.add(widget);
+ }
+
+ protected void renderEmptyResults() {
+ Grid grid = new Grid(1, 1);
+ grid.setWidget(0, 0, new HTML(I18nConstants.INSTANCE.noMeasures()));
+ grid.getCellFormatter().setStyleName(0, 0, getRowCssClass(0) + " emptyResultsCell");
+ grid.setStyleName("gwt-Hotspot");
+ render(grid);
+ }
+
+ protected void renderNameCell(Grid hotspotGrid, final Resource resource, final String metricKey, int row, int column) {
+ Anchor link = new Anchor(resource.getName());
+ link.getElement().setAttribute("title", resource.getName(true));
+ link.getElement().setAttribute("rel", resource.getName(true));
+ link.addClickHandler(new ClickHandler() {
+ public void onClick(final ClickEvent event) {
+ if (resource.getCopy() != null) {
+ Window.Location.assign(Links.baseUrl() + "/plugins/resource/" + resource.getCopy() + "?page=org.sonar.plugins.core.hotspots.GwtHotspots");
+ } else {
+ Links.openMeasurePopup(resource.getKey(), metricKey);
+ }
+ }
+ });
+ hotspotGrid.setWidget(row, column, link);
+ hotspotGrid.getCellFormatter().setStyleName(row, column, getRowCssClass(row) + " resourceCell");
+ }
+
+ protected void renderValueCell(Grid hotspotGrid, Measure measure, int row, int column) {
+ hotspotGrid.setHTML(row, column, measure.getFormattedValue());
+ hotspotGrid.getCellFormatter().setStyleName(row, column, getRowCssClass(row) + " resultCell");
+ }
+
+ protected void renderGraphCell(Grid hotspotGrid, Measure measure, Measure firstMeasure, int row, int column) {
+ Double value = Double.valueOf(measure.getValue());
+ Double upperValue = Double.valueOf(firstMeasure.getValue());
+ Double percentPonderated = getPercentPonderatedValue(value, 0d, upperValue);
+ String graph = "<span style='width:100%'><ul class='hbar' style='float: right;'><li style='background-color: rgb(119, 119, 119); width: " + percentPonderated.intValue() + "%'>&nbsp;</li></ul></span>";
+ hotspotGrid.setHTML(row, column, graph);
+ hotspotGrid.getCellFormatter().setStyleName(row, column, getRowCssClass(row) + " graphCell");
+ }
+
+ protected String getRowCssClass(int row) {
+ return row % 2 == 0 ? "even" : "odd";
+ }
+
+ protected double getPercentPonderatedValue(Double value, Double lower, Double upper) {
+ if (value < lower) return 0;
+ if (value > upper) return 100;
+ double percentIncrement = (upper - lower) / 100d;
+ return (value - lower) / percentIncrement;
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MetricHotspot.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MetricHotspot.java
new file mode 100644
index 00000000000..688cca9a08a
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MetricHotspot.java
@@ -0,0 +1,133 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.hotspots.client.widget;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.*;
+import org.sonar.gwt.Links;
+import org.sonar.plugins.core.hotspots.client.I18nConstants;
+import org.sonar.wsclient.gwt.AbstractListCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MetricHotspot extends AbstractHotspot {
+
+ private String metric;
+ private String title;
+
+ public MetricHotspot(Resource resource, String metric, String title) {
+ super(metric + "-hotspot", resource);
+ this.metric = metric;
+ this.title = title;
+ }
+
+ @Override
+ Widget createHeader() {
+ final Label label = new Label(title);
+ label.setStyleName("header");
+
+ final Anchor moreLink = new Anchor(I18nConstants.INSTANCE.moreDetails());
+ moreLink.getElement().setId("more-" + metric);
+ moreLink.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ Window.Location.assign(Links.baseUrl() + "/drilldown/measures/" + getResource().getKey() + "?metric=" + metric);
+ }
+ });
+
+ final HorizontalPanel horizontal = new HorizontalPanel();
+ horizontal.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);
+ horizontal.setWidth("98%");
+ horizontal.add(label);
+ horizontal.add(moreLink);
+ horizontal.setCellHorizontalAlignment(label, HorizontalPanel.ALIGN_LEFT);
+ horizontal.setCellHorizontalAlignment(moreLink, HorizontalPanel.ALIGN_RIGHT);
+
+ return horizontal;
+ }
+
+ @Override
+ void doLoadData() {
+ final ResourceQuery query = getResourceQuery();
+ Sonar.getInstance().findAll(query, new AbstractListCallback<Resource>() {
+
+ @Override
+ protected void doOnResponse(List<Resource> resources) {
+ List<HotspotMeasure> measures = new ArrayList<HotspotMeasure>();
+ for (Resource resource : resources) {
+ for (Measure measure : resource.getMeasures()) {
+ measures.add(new HotspotMeasure(resource, measure));
+ }
+ }
+
+ if (measures.isEmpty()) {
+ renderEmptyResults();
+
+ } else {
+ final Grid grid = new Grid(measures.size(), 3);
+ grid.setStyleName("gwt-Hotspot");
+ int row = 0;
+ HotspotMeasure firstMeasure = measures.get(0);
+ for (HotspotMeasure measure : measures) {
+ renderNameCell(grid, measure.getResource(), metric, row, 0);
+ renderValueCell(grid, measure.getMeasure(), row, 1);
+ renderGraphCell(grid, measure.getMeasure(), firstMeasure.getMeasure(), row, 2);
+ row++;
+ }
+
+ render(grid);
+ }
+ }
+ });
+ }
+
+ protected ResourceQuery getResourceQuery() {
+ return ResourceQuery.createForResource(getResource(), metric)
+ .setScopes(Resource.SCOPE_ENTITY)
+ .setDepth(ResourceQuery.DEPTH_UNLIMITED)
+ .setLimit(LIMIT);
+ }
+
+ public static class HotspotMeasure {
+ private Resource resource;
+ private Measure measure;
+
+ public HotspotMeasure(Resource resource, Measure measure) {
+ super();
+ this.resource = resource;
+ this.measure = measure;
+ }
+
+ public Resource getResource() {
+ return resource;
+ }
+
+ public Measure getMeasure() {
+ return measure;
+ }
+
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostBadlyDesignedFiles.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostBadlyDesignedFiles.java
new file mode 100644
index 00000000000..afbae117e5f
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostBadlyDesignedFiles.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.plugins.core.hotspots.client.widget;
+
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.*;
+import org.sonar.gwt.Links;
+import org.sonar.gwt.Metrics;
+import org.sonar.plugins.core.hotspots.client.I18nConstants;
+import org.sonar.wsclient.gwt.AbstractListCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+import java.util.List;
+
+public class MostBadlyDesignedFiles extends AbstractHotspot {
+
+ private ListBox metricSelectBox;
+
+ public MostBadlyDesignedFiles(Resource resource) {
+ super("design-hotspot", resource);
+ }
+
+ @Override
+ Widget createHeader() {
+ metricSelectBox = new ListBox(false);
+ metricSelectBox.addItem(I18nConstants.INSTANCE.lcom4(), "lcom4");
+ metricSelectBox.addItem(I18nConstants.INSTANCE.rfc(), "rfc");
+ metricSelectBox.setStyleName("small");
+ metricSelectBox.addChangeHandler(new ChangeHandler() {
+ public void onChange(ChangeEvent event) {
+ loadData();
+ }
+ });
+
+ final Label label = new Label(I18nConstants.INSTANCE.designTitle());
+ label.setStyleName("header");
+
+ final Anchor moreLink = new Anchor(I18nConstants.INSTANCE.moreDetails());
+ moreLink.getElement().setId("more-design");
+ moreLink.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ final String metric = getSelectedMetric();
+ Window.Location.assign(Links.baseUrl() + "/drilldown/measures/" + getResource().getId() + "?metric=" + metric);
+ }
+ });
+
+ final HorizontalPanel horizontal = new HorizontalPanel();
+ horizontal.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);
+ horizontal.setWidth("98%");
+ horizontal.add(label);
+ horizontal.add(metricSelectBox);
+ horizontal.add(moreLink);
+ horizontal.setCellHorizontalAlignment(label, HorizontalPanel.ALIGN_LEFT);
+ horizontal.setCellHorizontalAlignment(metricSelectBox, HorizontalPanel.ALIGN_LEFT);
+ horizontal.setCellHorizontalAlignment(moreLink, HorizontalPanel.ALIGN_RIGHT);
+
+ return horizontal;
+ }
+
+ @Override
+ void doLoadData() {
+ final ResourceQuery query = getResourceQuery();
+ Sonar.getInstance().findAll(query, new AbstractListCallback<Resource>() {
+
+ @Override
+ protected void doOnResponse(List<Resource> resources) {
+ final Grid grid = new Grid(resources.size(), 3);
+ grid.setStyleName("gwt-Hotspot");
+ int row = 0;
+ Measure firstMeasure = null;
+ for (Resource resource : resources) {
+ if (resource.getMeasures().size() == 1) {
+ if (firstMeasure == null) {
+ firstMeasure = resource.getMeasures().get(0);
+ }
+ renderNameCell(grid, resource, firstMeasure.getMetricKey(), row, 0);
+ renderValueCell(grid, resource.getMeasures().get(0), row, 1);
+ renderGraphCell(grid, resource.getMeasures().get(0), firstMeasure, row, 2);
+ row++;
+ }
+ }
+
+ if (firstMeasure == null) {
+ renderEmptyResults();
+ } else {
+ render(grid);
+ }
+ }
+ });
+ }
+
+ public ResourceQuery getResourceQuery() {
+ return ResourceQuery.createForResource(getResource(), getSelectedMetric())
+ .setDepth(-1)
+ .setQualifiers(Resource.QUALIFIER_CLASS)
+ .setLimit(LIMIT);
+ }
+
+ private String getSelectedMetric() {
+ return metricSelectBox.getValue(metricSelectBox.getSelectedIndex());
+ }
+}
+
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedResources.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedResources.java
new file mode 100644
index 00000000000..e79398ba063
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedResources.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.plugins.core.hotspots.client.widget;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.*;
+import org.sonar.gwt.Links;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.Icons;
+import org.sonar.plugins.core.hotspots.client.I18nConstants;
+import org.sonar.wsclient.gwt.AbstractListCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+import java.util.List;
+import java.util.Map;
+
+public class MostViolatedResources extends AbstractHotspot {
+
+ public MostViolatedResources(Resource resource) {
+ super("violated-files-hotspot", resource);
+ }
+
+ @Override
+ Widget createHeader() {
+ final Label label = new Label(I18nConstants.INSTANCE.titleMostViolatedResources());
+ label.setStyleName("header");
+
+ final Anchor moreLink = new Anchor(I18nConstants.INSTANCE.moreDetails());
+ moreLink.getElement().setId("more-violated-resources");
+ moreLink.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ Window.Location.assign(Links.baseUrl() + "/drilldown/measures/" + getResource().getId() + "?metric=" + Metrics.WEIGHTED_VIOLATIONS);
+ }
+ });
+
+ final HorizontalPanel horizontal = new HorizontalPanel();
+ horizontal.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);
+ horizontal.setWidth("98%");
+ horizontal.add(label);
+ horizontal.add(moreLink);
+ horizontal.setCellHorizontalAlignment(label, HorizontalPanel.ALIGN_LEFT);
+ horizontal.setCellHorizontalAlignment(moreLink, HorizontalPanel.ALIGN_RIGHT);
+
+ return horizontal;
+ }
+
+ @Override
+ void doLoadData() {
+ final ResourceQuery query = getResourceQuery();
+ Sonar.getInstance().findAll(query, new AbstractListCallback<Resource>() {
+
+ @Override
+ protected void doOnResponse(List<Resource> resources) {
+ Grid grid = new Grid(resources.size(), 11);
+ grid.setStyleName("gwt-Hotspot");
+ int row = 0;
+ for (Resource resource : resources) {
+ if (resource.getMeasures().size() > 0) {
+ renderNameCell(grid, resource, Metrics.WEIGHTED_VIOLATIONS, row, 0);
+ renderPriorities(grid, resource, row);
+ row++;
+ }
+ }
+ if (row == 0) {
+ renderEmptyResults();
+ } else {
+ render(grid);
+ }
+ }
+ });
+ }
+
+
+ private void renderPriorities(Grid grid, Resource resource, int row) {
+ Measure debt = resource.getMeasures().get(0);
+ if (debt != null && debt.getData() != null) {
+ Map<String, String> map = debt.getDataAsMap(";");
+ renderPriority(grid, row, map, 1, "BLOCKER");
+ renderPriority(grid, row, map, 3, "CRITICAL");
+ renderPriority(grid, row, map, 5, "MAJOR");
+ renderPriority(grid, row, map, 7, "MINOR");
+ renderPriority(grid, row, map, 9, "INFO");
+ }
+ }
+
+ private void renderPriority(Grid grid, int row, Map<String, String> map, int column, String priority) {
+ grid.setWidget(row, column, Icons.forPriority(priority).createImage());
+ grid.getCellFormatter().setStyleName(row, column, getRowCssClass(row) + " small right");
+
+ if (map.containsKey(priority)) {
+ grid.setWidget(row, column + 1, new HTML(map.get(priority)));
+ } else {
+ grid.setWidget(row, column + 1, new HTML("0"));
+ }
+ grid.getCellFormatter().setStyleName(row, column + 1, getRowCssClass(row) + " small left");
+ }
+
+ private ResourceQuery getResourceQuery() {
+ return ResourceQuery.createForResource(getResource(), Metrics.WEIGHTED_VIOLATIONS)
+ .setScopes(Resource.SCOPE_ENTITY)
+ .setQualifiers(Resource.QUALIFIER_CLASS, Resource.QUALIFIER_FILE, Resource.QUALIFIER_PROJECT)
+ .setDepth(ResourceQuery.DEPTH_UNLIMITED)
+ .setLimit(LIMIT);
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedRules.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedRules.java
new file mode 100644
index 00000000000..1255c1e5a63
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedRules.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.plugins.core.hotspots.client.widget;
+
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.*;
+import org.sonar.gwt.Links;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.Icons;
+import org.sonar.plugins.core.hotspots.client.I18nConstants;
+import org.sonar.wsclient.gwt.AbstractCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+public class MostViolatedRules extends AbstractHotspot {
+
+ private ListBox priorities;
+
+ public MostViolatedRules(Resource resource) {
+ super("rules-hotspot", resource);
+ }
+
+ @Override
+ Widget createHeader() {
+ priorities = new ListBox(false);
+ priorities.addItem(I18nConstants.INSTANCE.anyPriority(), "");
+ priorities.addItem("Blocker", "BLOCKER");
+ priorities.addItem("Critical", "CRITICAL");
+ priorities.addItem("Major", "MAJOR");
+ priorities.addItem("Minor", "MINOR");
+ priorities.addItem("Info", "INFO");
+ priorities.setStyleName("small");
+ priorities.addChangeHandler(new ChangeHandler() {
+ public void onChange(ChangeEvent event) {
+ loadData();
+ }
+ });
+
+ final Label label = new Label(I18nConstants.INSTANCE.titleMostViolatedRules());
+ label.setStyleName("header");
+
+ final Anchor moreLink = new Anchor(I18nConstants.INSTANCE.moreDetails());
+ moreLink.getElement().setId("more-rules");
+ moreLink.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ Window.Location.assign(Links.baseUrl() + "/drilldown/violations/" + getResource().getId());
+ }
+ });
+
+ final HorizontalPanel horizontal = new HorizontalPanel();
+ horizontal.setVerticalAlignment(HasAlignment.ALIGN_MIDDLE);
+ horizontal.setWidth("98%");
+ horizontal.add(label);
+ horizontal.add(priorities);
+ horizontal.add(moreLink);
+ horizontal.setCellHorizontalAlignment(label, HorizontalPanel.ALIGN_LEFT);
+ horizontal.setCellHorizontalAlignment(priorities, HorizontalPanel.ALIGN_LEFT);
+ horizontal.setCellHorizontalAlignment(moreLink, HorizontalPanel.ALIGN_RIGHT);
+
+ return horizontal;
+ }
+
+ @Override
+ void doLoadData() {
+ final ResourceQuery query = getResourceQuery();
+ Sonar.getInstance().find(query, new AbstractCallback<Resource>() {
+
+ @Override
+ protected void doOnResponse(Resource resource) {
+ if (resource.getMeasures().isEmpty()) {
+ renderEmptyResults();
+ } else {
+ renderGrid(resource);
+ }
+ }
+ });
+ }
+
+ private void renderGrid(Resource resource) {
+ final Grid grid = new Grid(resource.getMeasures().size(), 4);
+ grid.setStyleName("gwt-Hotspot");
+ int row = 0;
+ Measure firstMeasure = resource.getMeasures().get(0);
+ for (Measure measure : resource.getMeasures()) {
+ renderRule(grid, measure, row);
+ renderValueCell(grid, measure, row, 2);
+ renderGraphCell(grid, measure, firstMeasure, row, 3);
+ row++;
+ }
+ render(grid);
+ }
+
+ protected void renderRule(final Grid grid, final Measure measure, final int row) {
+ Anchor drillDown = new Anchor(measure.getRuleName());
+ drillDown.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ Window.Location.assign(Links.baseUrl() + "/drilldown/violations/" + getResource().getId() + "?rule=" + measure.getRuleKey());
+ }
+ });
+
+ grid.setWidget(row, 0, new HTML("<a id=\"rule" + row + "\" href=\"" + Links.urlForRule(measure.getRuleKey(), false) + "\" onclick=\"window.open(this.href,'rule','height=800,width=900,scrollbars=1,resizable=1');return false;\" title=\"" + measure.getRuleKey() + "\">" + Icons.forPriority(measure.getRulePriority()).getHTML() + "</a>"));
+ grid.setWidget(row, 1, drillDown);
+ grid.getCellFormatter().setStyleName(row, 0, getRowCssClass(row) + "");
+ grid.getCellFormatter().setStyleName(row, 1, getRowCssClass(row) + " resourceCell");
+ }
+
+ public ResourceQuery getResourceQuery() {
+ ResourceQuery query = ResourceQuery.createForResource(getResource(), Metrics.VIOLATIONS)
+ .setDepth(0)
+ .setExcludeRules(false)
+ .setLimit(LIMIT);
+ String priority = getSelectedPriority();
+ if (priority!=null) {
+ query.setRulePriorities(priority);
+ }
+ return query;
+ }
+
+ private String getSelectedPriority() {
+ String priority = priorities.getValue(priorities.getSelectedIndex());
+ if ("".equals(priority) || priority == null) {
+ return null;
+ }
+ return priority;
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/metrics/UserManagedMetrics.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/metrics/UserManagedMetrics.java
new file mode 100644
index 00000000000..d6a2bfbbe3f
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/metrics/UserManagedMetrics.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.plugins.core.metrics;
+
+import org.sonar.api.measures.Metric;
+import org.sonar.api.measures.Metrics;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class UserManagedMetrics implements Metrics {
+ public List<Metric> getMetrics() {
+ return Arrays.asList(
+ new Metric("burned_budget", "Burned budget", "The budget already used in the project", Metric.ValueType.FLOAT, 0, false, "Management", true),
+ new Metric("team_size", "Team size", "Size of the project team", Metric.ValueType.INT, 0, false, "Management", true),
+ new Metric("business_value", "Business value", "An indication on the value of the project for the business", Metric.ValueType.FLOAT, 1, true, "Management", true)
+ );
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeDeletedResources.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeDeletedResources.java
new file mode 100644
index 00000000000..63136a6086b
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeDeletedResources.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.plugins.core.purges;
+
+import org.sonar.core.purge.AbstractPurge;
+import org.sonar.api.batch.PurgeContext;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.ResourceModel;
+import org.sonar.api.database.model.Snapshot;
+
+import java.util.List;
+import javax.persistence.Query;
+
+/**
+ * @since 1.11
+ */
+public class PurgeDeletedResources extends AbstractPurge {
+
+ public PurgeDeletedResources(DatabaseSession session) {
+ super(session);
+ }
+
+ public void purge(PurgeContext context) {
+ Query query = getSession().createQuery("SELECT s.id FROM " + Snapshot.class.getSimpleName() +
+ " s WHERE NOT EXISTS(FROM " + ResourceModel.class.getSimpleName() + " r WHERE r.id=s.resourceId)");
+ final List<Integer> snapshotIds = query.getResultList();
+
+ deleteSnapshotData(snapshotIds);
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeDeprecatedLast.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeDeprecatedLast.java
new file mode 100644
index 00000000000..4e8c6a64c16
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeDeprecatedLast.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.plugins.core.purges;
+
+import org.sonar.core.purge.AbstractPurge;
+import org.sonar.api.batch.PurgeContext;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.Snapshot;
+
+import javax.persistence.Query;
+import java.util.List;
+
+/**
+ * @since 1.11
+ */
+public class PurgeDeprecatedLast extends AbstractPurge {
+
+ public PurgeDeprecatedLast(DatabaseSession session) {
+ super(session);
+ }
+
+ public void purge(PurgeContext context) {
+ Query query = getSession().createQuery("SELECT s.id FROM " + Snapshot.class.getSimpleName() +
+ " s WHERE s.last=true AND s.rootId IS NOT NULL AND NOT EXISTS(FROM " + Snapshot.class.getSimpleName() + " s2 WHERE s2.id=s.rootId AND s2.last=true)");
+ List<Integer> snapshotIds = query.getResultList();
+
+ deleteSnapshotData(snapshotIds);
+ }
+}
+
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeDisabledResources.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeDisabledResources.java
new file mode 100644
index 00000000000..bf1b571f469
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeDisabledResources.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.plugins.core.purges;
+
+import org.sonar.core.purge.AbstractPurge;
+import org.sonar.api.batch.PurgeContext;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.ResourceModel;
+import org.sonar.api.database.model.Snapshot;
+
+import java.util.List;
+import javax.persistence.Query;
+
+/**
+ * @since 1.11
+ */
+public class PurgeDisabledResources extends AbstractPurge {
+
+ public PurgeDisabledResources(DatabaseSession session) {
+ super(session);
+ }
+
+ public void purge(PurgeContext context) {
+ deleteSnapshotData(getSnapshotIds());
+ deleteResources();
+ }
+
+ private void deleteResources() {
+ final List<Integer> resourceIds = getResourceIds();
+ if (!resourceIds.isEmpty()) {
+ executeQuery(resourceIds, "delete from " + ResourceModel.class.getSimpleName() + " r where r.id in (:ids)");
+ }
+ }
+
+ private List<Integer> getResourceIds() {
+ Query query = getSession().createQuery("SELECT r.id FROM " + ResourceModel.class.getSimpleName() + " r WHERE r.enabled=false");
+ return (List<Integer>) query.getResultList();
+ }
+
+ private List<Integer> getSnapshotIds() {
+ Query query = getSession().createQuery("SELECT s.id FROM " + Snapshot.class.getSimpleName() + " s WHERE " +
+ " EXISTS (FROM " + ResourceModel.class.getSimpleName() + " r WHERE r.id=s.resourceId AND r.enabled=false)");
+ return (List<Integer>) query.getResultList();
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeEntities.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeEntities.java
new file mode 100644
index 00000000000..88b13381903
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeEntities.java
@@ -0,0 +1,53 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.purges;
+
+import org.apache.commons.lang.time.DateUtils;
+import org.sonar.core.purge.AbstractPurge;
+import org.sonar.api.batch.PurgeContext;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.Snapshot;
+import org.sonar.api.resources.Resource;
+
+import javax.persistence.Query;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @since 1.11
+ */
+public class PurgeEntities extends AbstractPurge {
+
+ private static final int MINIMUM_AGE_IN_HOURS = -12;
+
+ public PurgeEntities(DatabaseSession session) {
+ super(session);
+ }
+
+ public void purge(PurgeContext context) {
+ final Date beforeDate = DateUtils.addHours(new Date(), MINIMUM_AGE_IN_HOURS);
+ Query query = getSession().createQuery("SELECT s.id FROM " + Snapshot.class.getSimpleName() + " s WHERE s.last=false AND scope=:scope AND s.createdAt<:date");
+ query.setParameter("scope", Resource.SCOPE_ENTITY);
+ query.setParameter("date", beforeDate);
+ List<Integer> snapshotIds = query.getResultList();
+
+ deleteSnapshotData(snapshotIds);
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeEventOrphans.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeEventOrphans.java
new file mode 100644
index 00000000000..d98c17effa9
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeEventOrphans.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.plugins.core.purges;
+
+import org.sonar.core.purge.AbstractPurge;
+import org.sonar.api.batch.Event;
+import org.sonar.api.batch.PurgeContext;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.ResourceModel;
+
+import javax.persistence.Query;
+import java.util.List;
+
+public class PurgeEventOrphans extends AbstractPurge {
+
+ public PurgeEventOrphans(DatabaseSession session) {
+ super(session);
+ }
+
+ public void purge(PurgeContext context) {
+ Query query = getSession().createQuery("SELECT e.id FROM " + Event.class.getSimpleName() +
+ " e WHERE e.resourceId IS NOT NULL AND NOT EXISTS(FROM " + ResourceModel.class.getSimpleName() + " r WHERE r.id=e.resourceId)");
+ final List<Integer> eventIds = query.getResultList();
+ executeQuery(eventIds, "DELETE FROM " + Event.class.getSimpleName() + " WHERE id in (:ids)");
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeOrphanResources.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeOrphanResources.java
new file mode 100644
index 00000000000..bc720f8fcf0
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeOrphanResources.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.plugins.core.purges;
+
+import org.sonar.core.purge.AbstractPurge;
+import org.sonar.api.batch.PurgeContext;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.ResourceModel;
+
+import javax.persistence.Query;
+import java.util.List;
+
+/**
+ * @since 2.1
+ */
+public class PurgeOrphanResources extends AbstractPurge {
+
+ public PurgeOrphanResources(DatabaseSession session) {
+ super(session);
+ }
+
+ public void purge(PurgeContext context) {
+ Query query = getSession().createQuery("SELECT r1.id FROM " + ResourceModel.class.getSimpleName() +
+ " r1 WHERE r1.rootId IS NOT NULL AND NOT EXISTS(FROM " + ResourceModel.class.getSimpleName() + " r2 WHERE r1.rootId=r2.id)");
+ List<Integer> idsToDelete = query.getResultList();
+ if (idsToDelete.size() > 0) {
+ executeQuery(idsToDelete, "DELETE FROM " + ResourceModel.class.getSimpleName() + " WHERE id in (:ids)");
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgePropertyOrphans.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgePropertyOrphans.java
new file mode 100644
index 00000000000..e809dc2670c
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgePropertyOrphans.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.plugins.core.purges;
+
+import org.sonar.core.purge.AbstractPurge;
+import org.sonar.api.batch.PurgeContext;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.configuration.Property;
+import org.sonar.api.database.model.ResourceModel;
+import org.sonar.api.database.model.User;
+
+import javax.persistence.Query;
+import java.util.List;
+
+/**
+ * @since 2.2
+ */
+public class PurgePropertyOrphans extends AbstractPurge {
+
+ public PurgePropertyOrphans(DatabaseSession session) {
+ super(session);
+ }
+
+ public void purge(PurgeContext context) {
+ purgeResourceOrphans();
+ purgeUserOrphans();
+ }
+
+ void purgeResourceOrphans() {
+ Query query = getSession().createQuery("SELECT p.id FROM " + Property.class.getSimpleName() +
+ " p WHERE p.resourceId IS NOT NULL AND NOT EXISTS(FROM " + ResourceModel.class.getSimpleName() + " r WHERE r.id=p.resourceId)");
+ List<Integer> idsToDelete = query.getResultList();
+ if (idsToDelete.size() > 0) {
+ executeQuery(idsToDelete, "DELETE FROM " + Property.class.getSimpleName() + " WHERE id in (:ids)");
+ }
+ }
+
+ void purgeUserOrphans() {
+ Query query = getSession().createQuery("SELECT p.id FROM " + Property.class.getSimpleName() +
+ " p WHERE p.userId IS NOT NULL AND NOT EXISTS(FROM " + User.class.getSimpleName() + " u WHERE u.id=p.userId)");
+ List<Integer> idsToDelete = query.getResultList();
+ if (idsToDelete.size() > 0) {
+ executeQuery(idsToDelete, "DELETE FROM " + Property.class.getSimpleName() + " WHERE id in (:ids)");
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeResourceRoles.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeResourceRoles.java
new file mode 100644
index 00000000000..909301cc992
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeResourceRoles.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.plugins.core.purges;
+
+import org.sonar.core.purge.AbstractPurge;
+import org.sonar.api.batch.PurgeContext;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.ResourceModel;
+import org.sonar.api.security.GroupRole;
+import org.sonar.api.security.UserRole;
+
+import javax.persistence.Query;
+import java.util.List;
+
+/**
+ * @since 1.12
+ */
+public class PurgeResourceRoles extends AbstractPurge {
+
+ public PurgeResourceRoles(DatabaseSession session) {
+ super(session);
+ }
+
+ public void purge(PurgeContext context) {
+ deleteRoles(UserRole.class.getSimpleName());
+ deleteRoles(GroupRole.class.getSimpleName());
+ }
+
+ private void deleteRoles(String classname) {
+ Query query = getSession().createQuery("SELECT rol.id FROM " + classname + " rol "
+ + " WHERE rol.resourceId IS NOT NULL AND NOT EXISTS(FROM " + ResourceModel.class.getSimpleName() + " r WHERE r.id=rol.resourceId)");
+ List<Integer> roleIds = (List<Integer>) query.getResultList();
+ if (!roleIds.isEmpty()) {
+ executeQuery(roleIds, "delete from " + classname + " rol where rol.id in (:ids)");
+ }
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeRuleMeasures.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeRuleMeasures.java
new file mode 100644
index 00000000000..f2afd3b4b52
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeRuleMeasures.java
@@ -0,0 +1,56 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.purges;
+
+import org.sonar.core.purge.AbstractPurge;
+import org.sonar.api.batch.PurgeContext;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.MeasureModel;
+import org.sonar.api.database.model.Snapshot;
+
+import javax.persistence.Query;
+import java.util.List;
+
+/**
+ * see SONAR-522
+ *
+ * @since 1.11
+ */
+public class PurgeRuleMeasures extends AbstractPurge {
+
+ public PurgeRuleMeasures(DatabaseSession session) {
+ super(session);
+ }
+
+ public void purge(PurgeContext context) {
+ if (context.getPreviousSnapshotId() != null) {
+ purge(context.getPreviousSnapshotId());
+ }
+ }
+
+ private void purge(Integer sid) {
+ Query query = getSession().createQuery("SELECT m.id FROM " + MeasureModel.class.getSimpleName() + " m, " + Snapshot.class.getSimpleName() + " s WHERE s.id = m.snapshotId and " +
+ "(s.rootId=:rootSid OR s.id=:rootSid) and (m.rule is not null or m.rulesCategoryId is not null or m.rulePriority is not null)");
+ query.setParameter("rootSid", sid);
+ List<Integer> measureIds = query.getResultList();
+
+ deleteMeasuresById(measureIds);
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeUnprocessed.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeUnprocessed.java
new file mode 100644
index 00000000000..4f4a492f561
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/PurgeUnprocessed.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.plugins.core.purges;
+
+import org.apache.commons.lang.time.DateUtils;
+import org.sonar.core.purge.AbstractPurge;
+import org.sonar.api.batch.PurgeContext;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.Snapshot;
+
+import javax.persistence.Query;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @since 1.11
+ */
+public class PurgeUnprocessed extends AbstractPurge {
+
+ private static final int MINIMUM_AGE_IN_HOURS = -12;
+
+ public PurgeUnprocessed(DatabaseSession session) {
+ super(session);
+ }
+
+ public void purge(PurgeContext context) {
+ final Date beforeDate = DateUtils.addHours(new Date(), MINIMUM_AGE_IN_HOURS);
+ Query query = getSession().createQuery("SELECT s.id FROM " + Snapshot.class.getSimpleName() + " s WHERE s.last=false AND status=:status AND s.createdAt<:date");
+ query.setParameter("status", Snapshot.STATUS_UNPROCESSED);
+ query.setParameter("date", beforeDate);
+ List<Integer> snapshotIds = query.getResultList();
+
+ deleteSnapshotData(snapshotIds);
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/UnflagLastDoublons.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/UnflagLastDoublons.java
new file mode 100644
index 00000000000..da3c7a61202
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/purges/UnflagLastDoublons.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.plugins.core.purges;
+
+import org.sonar.api.batch.PurgeContext;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.database.model.Snapshot;
+import org.sonar.core.purge.AbstractPurge;
+
+import javax.persistence.Query;
+import java.util.List;
+
+/**
+ * @since 1.11
+ */
+public class UnflagLastDoublons extends AbstractPurge {
+
+ public UnflagLastDoublons(DatabaseSession session) {
+ super(session);
+ }
+
+ public void purge(PurgeContext context) {
+ Query query = getSession().createQuery(
+ "SELECT olds.id FROM " + Snapshot.class.getSimpleName() + " olds " +
+ " where olds.last=true AND EXISTS (from " + Snapshot.class.getSimpleName() + " news WHERE news.last=true AND news.resourceId=olds.resourceId AND news.createdAt>olds.createdAt)");
+ List<Integer> snapshotIds = query.getResultList();
+
+ executeQuery(snapshotIds, "UPDATE " + Snapshot.class.getSimpleName() + " SET last=false WHERE id in (:ids)");
+ }
+}
+
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/security/ApplyProjectRolesDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/security/ApplyProjectRolesDecorator.java
new file mode 100644
index 00000000000..2f9f2e74779
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/security/ApplyProjectRolesDecorator.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.plugins.core.security;
+
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+
+public class ApplyProjectRolesDecorator implements Decorator {
+
+ private RoleManager roleManager;
+
+ protected ApplyProjectRolesDecorator(RoleManager roleManager) {
+ this.roleManager = roleManager;
+ }
+
+ public ApplyProjectRolesDecorator(DatabaseSession session) {
+ this.roleManager = new RoleManager(session);
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (shouldDecorateResource(resource)) {
+ Project project = (Project) resource;
+ roleManager.affectDefaultRolesToResource(project.getId());
+ }
+ }
+
+ private boolean shouldDecorateResource(Resource resource) {
+ if (isProject(resource)) {
+ Project project = (Project) resource;
+ return project.getId() != null && countRoles(project.getId()) == 0;
+ }
+ return false;
+ }
+
+ private boolean isProject(Resource resource) {
+ if (Resource.QUALIFIER_PROJECT.equals(resource.getQualifier()) ||
+ Resource.QUALIFIER_VIEW.equals(resource.getQualifier()) ||
+ Resource.QUALIFIER_SUBVIEW.equals(resource.getQualifier())) {
+ return resource instanceof Project;
+ }
+ return false;
+ }
+
+ private int countRoles(int resourceId) {
+ return roleManager.getUserRoles(resourceId).size() + roleManager.getGroupRoles(resourceId).size();
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/security/RoleManager.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/security/RoleManager.java
new file mode 100644
index 00000000000..12bea9448a2
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/security/RoleManager.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.plugins.core.security;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.database.DatabaseSession;
+import org.sonar.api.security.GroupRole;
+import org.sonar.api.security.UserRole;
+
+import javax.persistence.Query;
+import java.util.List;
+
+/**
+ * @since 1.12
+ */
+public class RoleManager {
+
+ protected static final String DEFAULT_ROLE_PREFIX = "default-";
+ private DatabaseSession session;
+
+ public RoleManager(DatabaseSession session) {
+ this.session = session;
+ }
+
+ public void affectDefaultRolesToResource(int resourceId) {
+ for (UserRole defaultRole : getDefaultUserRoles()) {
+ session.save(createResourceRoleFromDefault(defaultRole, resourceId));
+ }
+ for (GroupRole defaultRole : getDefaultGroupRoles()) {
+ session.save(createResourceRoleFromDefault(defaultRole, resourceId));
+ }
+ session.commit();
+ }
+
+ public List<UserRole> getUserRoles(int resourceId) {
+ return session.getResults(UserRole.class, "resourceId", resourceId);
+ }
+
+ public List<GroupRole> getGroupRoles(int resourceId) {
+ return session.getResults(GroupRole.class, "resourceId", resourceId);
+ }
+
+ protected List<UserRole> getDefaultUserRoles() {
+ final Query query = session.createQuery("from " + UserRole.class.getSimpleName() + " ur where ur.resourceId is null and ur.role like '" + DEFAULT_ROLE_PREFIX + "%'");
+ return query.getResultList();
+ }
+
+ protected List<GroupRole> getDefaultGroupRoles() {
+ final Query query = session.createQuery("from " + GroupRole.class.getSimpleName() + " gr where gr.resourceId is null and gr.role like '" + DEFAULT_ROLE_PREFIX + "%'");
+ return query.getResultList();
+ }
+
+ protected UserRole createResourceRoleFromDefault(UserRole defaultUserRole, int resourceId) {
+ final UserRole result = new UserRole();
+ result.setRole(convertDefaultRoleName(defaultUserRole.getRole()));
+ result.setResourceId(resourceId);
+ result.setUserId(defaultUserRole.getUserId());
+ return result;
+ }
+
+ protected GroupRole createResourceRoleFromDefault(GroupRole defaultUserRole, int resourceId) {
+ final GroupRole result = new GroupRole();
+ result.setRole(convertDefaultRoleName(defaultUserRole.getRole()));
+ result.setResourceId(resourceId);
+ result.setGroupId(defaultUserRole.getGroupId());
+ return result;
+ }
+
+ protected static String convertDefaultRoleName(String defaultRoleName) {
+ return StringUtils.substringAfter(defaultRoleName, DEFAULT_ROLE_PREFIX);
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/AbstractCoverageDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/AbstractCoverageDecorator.java
new file mode 100644
index 00000000000..d6bc7c87828
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/AbstractCoverageDecorator.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.plugins.core.sensors;
+
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
+
+public abstract class AbstractCoverageDecorator implements Decorator {
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return project.getAnalysisType().isDynamic(true);
+ }
+
+ @DependedUpon
+ public Metric generatesCoverage() {
+ return getTargetMetric();
+ }
+
+ public void decorate(final Resource resource, final DecoratorContext context) {
+ if (shouldDecorate(resource, context)) {
+ saveLineCoverage(context);
+ }
+ }
+
+ protected boolean shouldDecorate(final Resource resource, final DecoratorContext context) {
+ return context.getMeasure(getTargetMetric()) == null && !ResourceUtils.isUnitTestClass(resource);
+ }
+
+ private void saveLineCoverage(DecoratorContext context) {
+ Double elements = countElements(context);
+ Double coveredElements = countCoveredElements(context);
+
+ if (elements != null && elements > 0.0 && coveredElements != null) {
+ context.saveMeasure(getTargetMetric(), calculateCoverage(coveredElements, elements));
+ }
+ }
+
+ private double calculateCoverage(final Double coveredLines, final Double lines) {
+ return (100.0 * coveredLines) / lines;
+ }
+
+ protected abstract Metric getTargetMetric();
+
+ protected abstract Double countCoveredElements(DecoratorContext context);
+
+ protected abstract Double countElements(DecoratorContext context);
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/AlertUtils.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/AlertUtils.java
new file mode 100644
index 00000000000..a9741b43eb1
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/AlertUtils.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.plugins.core.sensors;
+
+import org.apache.commons.lang.NotImplementedException;
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.profiles.Alert;
+
+public final class AlertUtils {
+ private AlertUtils() {
+ }
+
+ /**
+ * Get the matching alert level for the given measure
+ */
+ public static Metric.Level getLevel(Alert alert, Measure measure) {
+ if (evaluateAlert(alert, measure, Metric.Level.ERROR)) {
+ return Metric.Level.ERROR;
+ }
+ if (evaluateAlert(alert, measure, Metric.Level.WARN)) {
+ return Metric.Level.WARN;
+ }
+ return Metric.Level.OK;
+ }
+
+ private static boolean evaluateAlert(Alert alert, Measure measure, Metric.Level alertLevel) {
+ String valueToEval;
+ if (alertLevel.equals(Metric.Level.ERROR)) {
+ valueToEval = alert.getValueError();
+
+ } else if (alertLevel.equals(Metric.Level.WARN)) {
+ valueToEval = alert.getValueWarning();
+ } else {
+ throw new IllegalStateException(alertLevel.toString());
+ }
+ if (StringUtils.isEmpty(valueToEval)) {
+ return false;
+ }
+
+ Comparable criteriaValue = getValueForComparison(alert.getMetric(), valueToEval);
+ Comparable metricValue = getMeasureValue(alert.getMetric(), measure);
+
+ int comparison = metricValue.compareTo(criteriaValue);
+ if (alert.isNotEqualsOperator() && comparison == 0 ||
+ alert.isGreaterOperator() && comparison != 1 ||
+ alert.isSmallerOperator() && comparison != -1 ||
+ alert.isEqualsOperator() && comparison != 0) {
+ return false;
+ }
+ return true;
+ }
+
+
+ private static Comparable<?> getValueForComparison(Metric metric, String value) {
+ if (metric.getType() == Metric.ValueType.FLOAT ||
+ metric.getType() == Metric.ValueType.PERCENT) {
+ return Double.parseDouble(value);
+ } else if (metric.getType() == Metric.ValueType.INT ||
+ metric.getType() == Metric.ValueType.MILLISEC) {
+ return value.contains(".") ? Integer.parseInt(value.substring(0, value.indexOf('.'))) : Integer.parseInt(value);
+ } else if (metric.getType() == Metric.ValueType.STRING ||
+ metric.getType() == Metric.ValueType.LEVEL) {
+ return value;
+ } else if (metric.getType() == Metric.ValueType.BOOL) {
+ return Boolean.valueOf(value);
+ }
+ throw new NotImplementedException(metric.getType().toString());
+ }
+
+ private static Comparable<?> getMeasureValue(Metric metric, Measure measure) {
+ if (metric.getType() == Metric.ValueType.FLOAT ||
+ metric.getType() == Metric.ValueType.PERCENT) {
+ return measure.getValue();
+ } else if (metric.getType() == Metric.ValueType.INT ||
+ metric.getType() == Metric.ValueType.MILLISEC) {
+ return measure.getValue().intValue();
+ } else if (metric.getType() == Metric.ValueType.STRING ||
+ metric.getType() == Metric.ValueType.LEVEL) {
+ return measure.getData();
+ } else if (metric.getType() == Metric.ValueType.BOOL) {
+ return measure.getValue() == 0d ? Boolean.FALSE : Boolean.TRUE;
+ }
+ throw new NotImplementedException(metric.getType().toString());
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/AsynchronousMeasuresSensor.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/AsynchronousMeasuresSensor.java
new file mode 100644
index 00000000000..aef6430ae37
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/AsynchronousMeasuresSensor.java
@@ -0,0 +1,56 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.sensors;
+
+import org.sonar.api.batch.Phase;
+import org.sonar.api.batch.Sensor;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.jpa.dao.AsyncMeasuresService;
+import org.sonar.api.database.model.Snapshot;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.ResourceUtils;
+
+@Phase(name = Phase.Name.PRE)
+public class AsynchronousMeasuresSensor implements Sensor {
+
+ private AsyncMeasuresService reviewsService;
+ private Snapshot snapshot;
+
+ public AsynchronousMeasuresSensor(AsyncMeasuresService reviewsService, Snapshot snapshot) {
+ this.reviewsService = reviewsService;
+ this.snapshot = snapshot;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ public void analyse(Project project, SensorContext context) {
+ if (!ResourceUtils.isRootProject(project)) {
+ return;
+ }
+ reviewsService.refresh(snapshot);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/BranchCoverageDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/BranchCoverageDecorator.java
new file mode 100644
index 00000000000..4102ec48e18
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/BranchCoverageDecorator.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.plugins.core.sensors;
+
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.MeasureUtils;
+import org.sonar.api.measures.Metric;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class BranchCoverageDecorator extends AbstractCoverageDecorator {
+
+ @Override
+ protected Metric getTargetMetric() {
+ return CoreMetrics.BRANCH_COVERAGE;
+ }
+
+ @DependsUpon
+ public List<Metric> dependsUponMetrics() {
+ return Arrays.asList(CoreMetrics.UNCOVERED_CONDITIONS, CoreMetrics.CONDITIONS_TO_COVER);
+ }
+
+ @Override
+ protected Double countCoveredElements(DecoratorContext context) {
+ double uncoveredConditions = MeasureUtils.getValue(context.getMeasure(CoreMetrics.UNCOVERED_CONDITIONS), 0.0);
+ double conditions = MeasureUtils.getValue(context.getMeasure(CoreMetrics.CONDITIONS_TO_COVER), 0.0);
+ return conditions - uncoveredConditions;
+ }
+
+ @Override
+ protected Double countElements(DecoratorContext context) {
+ return MeasureUtils.getValue(context.getMeasure(CoreMetrics.CONDITIONS_TO_COVER), 0.0);
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CheckAlertThresholds.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CheckAlertThresholds.java
new file mode 100644
index 00000000000..8e55fa3890d
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CheckAlertThresholds.java
@@ -0,0 +1,116 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.sensors;
+
+import org.apache.commons.lang.StringUtils;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.Phase;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.profiles.Alert;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Phase(name = Phase.Name.POST)
+public class CheckAlertThresholds implements Decorator {
+
+ private final RulesProfile profile;
+
+ public CheckAlertThresholds(RulesProfile profile) {
+ this.profile = profile;
+ }
+
+ @DependedUpon
+ public Metric generatesAlertStatus() {
+ return CoreMetrics.ALERT_STATUS;
+ }
+
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return profile != null
+ && profile.getAlerts() != null
+ && profile.getAlerts().size() > 0
+ && ResourceUtils.isRootProject(project);
+ }
+
+ public void decorate(final Resource resource, final DecoratorContext context) {
+ if (shouldDecorateResource(resource)) {
+ decorateResource(context);
+ }
+ }
+
+ private void decorateResource(DecoratorContext context) {
+ Metric.Level globalLevel = Metric.Level.OK;
+ List<String> labels = new ArrayList<String>();
+
+ for (final Alert alert : profile.getAlerts()) {
+ Measure measure = context.getMeasure(alert.getMetric());
+ if (measure != null) {
+ Metric.Level level = AlertUtils.getLevel(alert, measure);
+
+ measure.setAlertStatus(level);
+ String text = getText(alert, level);
+ if (!StringUtils.isBlank(text)) {
+ measure.setAlertText(text);
+ labels.add(text);
+ }
+
+ context.saveMeasure(measure);
+
+ if (Metric.Level.WARN == level && globalLevel != Metric.Level.ERROR) {
+ globalLevel = Metric.Level.WARN;
+
+ } else if (Metric.Level.ERROR == level) {
+ globalLevel = Metric.Level.ERROR;
+ }
+ }
+ }
+
+ Measure globalMeasure = new Measure(CoreMetrics.ALERT_STATUS, globalLevel);
+ globalMeasure.setAlertStatus(globalLevel);
+ globalMeasure.setAlertText(StringUtils.join(labels, ", "));
+ context.saveMeasure(globalMeasure);
+ }
+
+ private boolean shouldDecorateResource(final Resource resource) {
+ return ResourceUtils.isRootProject(resource);
+ }
+
+ private String getText(Alert alert, Metric.Level level) {
+ if (level == Metric.Level.OK) {
+ return null;
+ }
+ return alert.getAlertLabel(level);
+ }
+
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CommentDensityDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CommentDensityDecorator.java
new file mode 100644
index 00000000000..0d9d579eb0c
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CommentDensityDecorator.java
@@ -0,0 +1,91 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.sensors;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.measures.CoreMetrics;
+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;
+
+public class CommentDensityDecorator implements Decorator {
+
+ @DependsUpon
+ public List<Metric> dependsUponMetrics() {
+ return Arrays.asList(CoreMetrics.NCLOC, CoreMetrics.COMMENT_LINES, CoreMetrics.PUBLIC_API, CoreMetrics.PUBLIC_UNDOCUMENTED_API);
+ }
+
+ @DependedUpon
+ public List<Metric> generatesMetrics() {
+ return Arrays.asList(CoreMetrics.COMMENT_LINES_DENSITY, CoreMetrics.PUBLIC_DOCUMENTED_API_DENSITY);
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ saveCommentsDensity(context);
+ savePublicApiDensity(context);
+ }
+
+ private void saveCommentsDensity(DecoratorContext context) {
+ if (context.getMeasure(CoreMetrics.COMMENT_LINES_DENSITY) != null) {
+ return;
+ }
+
+ Measure ncloc = context.getMeasure(CoreMetrics.NCLOC);
+ Measure comments = context.getMeasure(CoreMetrics.COMMENT_LINES);
+ if (MeasureUtils.hasValue(ncloc) && MeasureUtils.hasValue(comments)) {
+ if (comments.getValue() + ncloc.getValue() > 0) {
+ double val = 100.0 * (comments.getValue() / (comments.getValue() + ncloc.getValue()));
+ context.saveMeasure(new Measure(CoreMetrics.COMMENT_LINES_DENSITY, val));
+ }
+ }
+ }
+
+ private void savePublicApiDensity(DecoratorContext context) {
+ if (context.getMeasure(CoreMetrics.PUBLIC_DOCUMENTED_API_DENSITY) != null) {
+ return;
+ }
+
+ Measure publicApi = context.getMeasure(CoreMetrics.PUBLIC_API);
+ Measure publicUndocApi = context.getMeasure(CoreMetrics.PUBLIC_UNDOCUMENTED_API);
+
+ if (MeasureUtils.hasValue(publicApi) && MeasureUtils.hasValue(publicUndocApi) && publicApi.getValue() > 0) {
+ double documentedAPI = publicApi.getValue() - publicUndocApi.getValue();
+ Double value = 100.0 * (documentedAPI / publicApi.getValue());
+ context.saveMeasure(new Measure(CoreMetrics.PUBLIC_DOCUMENTED_API_DENSITY, value));
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CoverageDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CoverageDecorator.java
new file mode 100644
index 00000000000..559d61e3c43
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/CoverageDecorator.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.plugins.core.sensors;
+
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.MeasureUtils;
+import org.sonar.api.measures.Metric;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class CoverageDecorator extends AbstractCoverageDecorator {
+
+ @Override
+ protected Metric getTargetMetric() {
+ return CoreMetrics.COVERAGE;
+ }
+
+ @DependsUpon
+ public List<Metric> dependsUponMetrics() {
+ return Arrays.asList(CoreMetrics.LINES_TO_COVER, CoreMetrics.UNCOVERED_LINES, CoreMetrics.CONDITIONS_TO_COVER, CoreMetrics.UNCOVERED_CONDITIONS);
+ }
+
+ @Override
+ protected Double countCoveredElements(DecoratorContext context) {
+ double uncoveredLines = MeasureUtils.getValue(context.getMeasure(CoreMetrics.UNCOVERED_LINES), 0.0);
+ double lines = MeasureUtils.getValue(context.getMeasure(CoreMetrics.LINES_TO_COVER), 0.0);
+ double uncoveredConditions = MeasureUtils.getValue(context.getMeasure(CoreMetrics.UNCOVERED_CONDITIONS), 0.0);
+ double conditions = MeasureUtils.getValue(context.getMeasure(CoreMetrics.CONDITIONS_TO_COVER), 0.0);
+ return lines + conditions - uncoveredConditions - uncoveredLines;
+ }
+
+ @Override
+ protected Double countElements(DecoratorContext context) {
+ double lines = MeasureUtils.getValue(context.getMeasure(CoreMetrics.LINES_TO_COVER), 0.0);
+ double conditions = MeasureUtils.getValue(context.getMeasure(CoreMetrics.CONDITIONS_TO_COVER), 0.0);
+ return lines + conditions;
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/DirectoriesDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/DirectoriesDecorator.java
new file mode 100644
index 00000000000..5df50ef6028
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/DirectoriesDecorator.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.plugins.core.sensors;
+
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.measures.CoreMetrics;
+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 org.sonar.api.resources.ResourceUtils;
+
+import java.util.Collection;
+
+/**
+ * @since 2.2
+ */
+public final class DirectoriesDecorator implements Decorator {
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ @DependedUpon
+ public Metric generateDirectoriesMetric() {
+ return CoreMetrics.DIRECTORIES;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (MeasureUtils.hasValue(context.getMeasure(CoreMetrics.DIRECTORIES))) {
+ return;
+ }
+
+ if (Resource.QUALIFIER_DIRECTORY.equals(resource.getQualifier())) {
+ context.saveMeasure(CoreMetrics.DIRECTORIES, 1.0);
+
+ } else if (ResourceUtils.isSet(resource) || ResourceUtils.isView(resource) || ResourceUtils.isSubview(resource)) {
+ Collection<Measure> childrenMeasures = context.getChildrenMeasures(CoreMetrics.DIRECTORIES);
+ Double sum = MeasureUtils.sum(false, childrenMeasures);
+ if (sum != null) {
+ context.saveMeasure(CoreMetrics.DIRECTORIES, sum);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/FilesDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/FilesDecorator.java
new file mode 100644
index 00000000000..124690647e9
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/FilesDecorator.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.plugins.core.sensors;
+
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.measures.CoreMetrics;
+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.Collection;
+
+/**
+ * @since 2.2
+ */
+public final class FilesDecorator implements Decorator {
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ @DependedUpon
+ public Metric generateDirectoriesMetric() {
+ return CoreMetrics.FILES;
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (MeasureUtils.hasValue(context.getMeasure(CoreMetrics.FILES))) {
+ return;
+ }
+
+ if (Resource.QUALIFIER_CLASS.equals(resource.getQualifier()) || Resource.QUALIFIER_FILE.equals(resource.getQualifier())) {
+ context.saveMeasure(CoreMetrics.FILES, 1.0);
+
+ } else {
+ Collection<Measure> childrenMeasures = context.getChildrenMeasures(CoreMetrics.FILES);
+ Double sum = MeasureUtils.sum(false, childrenMeasures);
+ if (sum != null) {
+ context.saveMeasure(CoreMetrics.FILES, sum);
+ }
+ }
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/GenerateAlertEvents.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/GenerateAlertEvents.java
new file mode 100644
index 00000000000..f8af4708d3f
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/GenerateAlertEvents.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.plugins.core.sensors;
+
+import org.sonar.api.batch.*;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
+
+import java.util.List;
+
+@Phase(name = Phase.Name.POST)
+public class GenerateAlertEvents implements Decorator {
+
+ private final RulesProfile profile;
+ private final TimeMachine timeMachine;
+
+ public GenerateAlertEvents(RulesProfile profile, TimeMachine timeMachine) {
+ this.profile = profile;
+ this.timeMachine = timeMachine;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return profile != null && profile.getAlerts() != null && profile.getAlerts().size() > 0;
+ }
+
+
+ @DependsUpon
+ public Metric dependsUponAlertStatus() {
+ return CoreMetrics.ALERT_STATUS;
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (!shouldDecorateResource(resource)) {
+ return;
+ }
+ Measure currentStatus = context.getMeasure(CoreMetrics.ALERT_STATUS);
+ if (currentStatus == null) {
+ return;
+ }
+
+ TimeMachineQuery query = new TimeMachineQuery(resource).setOnlyLastAnalysis(true).setMetrics(CoreMetrics.ALERT_STATUS);
+ List<Measure> measures = timeMachine.getMeasures(query);
+
+ Measure pastStatus = (measures != null && measures.size() == 1 ? measures.get(0) : null);
+ if (pastStatus != null && pastStatus.getDataAsLevel() != currentStatus.getDataAsLevel()) {
+ createEvent(context, getName(pastStatus, currentStatus), currentStatus.getAlertText());
+
+ } else if (pastStatus == null && currentStatus.getDataAsLevel() != Metric.Level.OK) {
+ createEvent(context, getName(currentStatus), currentStatus.getAlertText());
+ }
+
+ }
+
+ private boolean shouldDecorateResource(Resource resource) {
+ return ResourceUtils.isRootProject(resource);
+ }
+
+ private String getName(Measure pastStatus, Measure currentStatus) {
+ return getName(currentStatus) + " (was " + getName(pastStatus) + ")";
+
+ }
+
+ private String getName(Measure currentStatus) {
+ return currentStatus.getDataAsLevel().getColorName();
+ }
+
+ private void createEvent(DecoratorContext context, String name, String description) {
+ context.createEvent(name, description, Event.CATEGORY_ALERT, null);
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/JavaSourceImporter.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/JavaSourceImporter.java
new file mode 100644
index 00000000000..b54a223fced
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/JavaSourceImporter.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.plugins.core.sensors;
+
+import org.sonar.api.batch.AbstractSourceImporter;
+import org.sonar.api.batch.Phase;
+import org.sonar.api.resources.Java;
+import org.sonar.api.resources.JavaFile;
+import org.sonar.api.resources.Resource;
+
+import java.io.File;
+import java.util.List;
+
+@Phase(name = Phase.Name.PRE)
+public class JavaSourceImporter extends AbstractSourceImporter {
+
+ public JavaSourceImporter() {
+ super(Java.INSTANCE);
+ }
+
+ @Override
+ protected Resource createResource(File file, List<File> sourceDirs, boolean unitTest) {
+ return (file != null && !file.getName().contains("$")) ? JavaFile.fromIOFile(file, sourceDirs, unitTest) : null;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/LineCoverageDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/LineCoverageDecorator.java
new file mode 100644
index 00000000000..c97cc7784bf
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/LineCoverageDecorator.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.plugins.core.sensors;
+
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.MeasureUtils;
+import org.sonar.api.measures.Metric;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class LineCoverageDecorator extends AbstractCoverageDecorator {
+
+ @Override
+ protected Metric getTargetMetric() {
+ return CoreMetrics.LINE_COVERAGE;
+ }
+
+ @DependsUpon
+ public List<Metric> dependsUponMetrics() {
+ return Arrays.asList(CoreMetrics.UNCOVERED_LINES, CoreMetrics.LINES_TO_COVER);
+ }
+
+ @Override
+ protected Double countCoveredElements(DecoratorContext context) {
+ double uncoveredLines = MeasureUtils.getValue(context.getMeasure(CoreMetrics.UNCOVERED_LINES), 0.0);
+ double lines = MeasureUtils.getValue(context.getMeasure(CoreMetrics.LINES_TO_COVER), 0.0);
+ return lines - uncoveredLines;
+ }
+
+ @Override
+ protected Double countElements(DecoratorContext context) {
+ return MeasureUtils.getValue(context.getMeasure(CoreMetrics.LINES_TO_COVER), 0.0);
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ProfileSensor.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ProfileSensor.java
new file mode 100644
index 00000000000..661ea9f83d7
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ProfileSensor.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.plugins.core.sensors;
+
+import org.sonar.api.batch.Sensor;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.resources.Project;
+
+public class ProfileSensor implements Sensor {
+
+ private RulesProfile profile;
+
+ public ProfileSensor(RulesProfile profile) {
+ this.profile = profile;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ public void analyse(Project project, SensorContext context) {
+ if (profile != null) {
+ Measure measure = new Measure(CoreMetrics.PROFILE, profile.getName());
+ if (profile.getId() != null) {
+ measure.setValue(profile.getId().doubleValue());
+ }
+ context.saveMeasure(measure);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ProjectLinksSensor.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ProjectLinksSensor.java
new file mode 100644
index 00000000000..32e1503a991
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ProjectLinksSensor.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.plugins.core.sensors;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.maven.model.CiManagement;
+import org.apache.maven.model.IssueManagement;
+import org.apache.maven.model.Scm;
+import org.apache.maven.project.MavenProject;
+import org.sonar.api.batch.Sensor;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.ProjectLink;
+
+public class ProjectLinksSensor implements Sensor {
+
+ public static final String KEY_HOME = "homepage";
+ public static final String KEY_CONTINUOUS_INTEGRATION = "ci";
+ public static final String KEY_ISSUE_TRACKER = "issue";
+ public static final String KEY_SCM = "scm";
+ public static final String KEY_SCM_DEVELOPER_CONNECTION = "scm_dev";
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ public void analyse(Project project, SensorContext context) {
+ MavenProject pom = project.getPom();
+ updateLink(context, KEY_HOME, "Home", pom.getUrl());
+
+ Scm scm = pom.getScm();
+ if (scm == null) {
+ scm = new Scm();
+ }
+ updateLink(context, KEY_SCM, "Sources", scm.getUrl());
+ updateLink(context, KEY_SCM_DEVELOPER_CONNECTION, "Developer connection", scm.getDeveloperConnection());
+
+
+ CiManagement ci = pom.getCiManagement();
+ if (ci == null) {
+ ci = new CiManagement();
+ }
+ updateLink(context, KEY_CONTINUOUS_INTEGRATION, "Continuous integration", ci.getUrl());
+
+ IssueManagement issues = pom.getIssueManagement();
+ if (issues == null) {
+ issues = new IssueManagement();
+ }
+ updateLink(context, KEY_ISSUE_TRACKER, "Issues", issues.getUrl());
+ }
+
+ private void updateLink(SensorContext context, String key, String name, String url) {
+ if (StringUtils.isBlank(url)) {
+ context.deleteLink(key);
+ } else {
+ context.saveLink(new ProjectLink(key, name, url));
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/TendencyAnalyser.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/TendencyAnalyser.java
new file mode 100644
index 00000000000..0ffde81e912
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/TendencyAnalyser.java
@@ -0,0 +1,191 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.sensors;
+
+import java.util.List;
+
+public class TendencyAnalyser {
+
+ public final static Integer TENDENCY_BIG_UP = 2;
+ public final static Integer TENDENCY_UP = 1;
+ public final static Integer TENDENCY_NEUTRAL = 0;
+ public final static Integer TENDENCY_DOWN = -1;
+ public final static Integer TENDENCY_BIG_DOWN = -2;
+
+ public Integer analyseLevel(List<Double> values) {
+ TendencyAnalyser.SlopeData slopeData = analyse(values);
+ if (slopeData != null) {
+ return slopeData.getLevel();
+ }
+ return null;
+ }
+
+ public SlopeData analyse(List<Double> values) {
+ double sumY = 0.0;
+ double sumX = 0.0;
+ double sumYPower2 = 0.0;
+ double sumXY = 0.0;
+ double sumXPower2 = 0.0;
+ int nbrPoints = 0;
+ boolean nullValuesYList = true;
+ int i = 0;
+ for (Double p : values) {
+ if (p != null) {
+ nullValuesYList = false;
+ //SumY calculation
+ sumY += p;
+ // sumYPower2 calculation
+ sumYPower2 += p * p;
+ //sumXY calculation
+ sumXY += p * (i + 1);
+ //SumX calculation
+ sumX += (i + 1);
+ //sumXPower2 calculation
+ sumXPower2 += (i + 1) * (i + 1);
+ //Point number calculation
+ nbrPoints++;
+ }
+ i++;
+ }
+ // no tendency if null values or only 1 value
+ if (nullValuesYList || nbrPoints == 1) {
+ return null;
+ }
+ double n0 = (((nbrPoints) * (sumXY)) - ((sumX) * (sumY)));
+ double d = (((nbrPoints) * (sumXPower2)) - ((sumX) * (sumX)));
+ double n1 = (((sumY) * (sumXPower2)) - ((sumX) * (sumXY)));
+
+ SlopeData result = new SlopeData();
+
+ //yIntercept Calculation the value when X equals zero
+ result.setYIntercept(n1 / d);
+ // Slope Calculation
+ if (n0 == 0d && d == 0d) {
+ result.setSlope(0.0);
+ } else {
+ Double slope = n0 / d;
+ if (Double.isNaN(slope) || Double.isInfinite(slope)) {
+ result.setSlope(null);
+ } else {
+ result.setSlope(slope);
+ }
+ }
+ result.setSumXPower2(sumXPower2);
+ result.setSumXY(sumXY);
+ result.setSumYPower2(sumYPower2);
+
+ if (sumXPower2 == 0 || sumYPower2 == 0) {
+ result.setCorrelationRate(0.0);
+ } else {
+ result.setCorrelationRate((sumXY) / (Math.sqrt(sumXPower2 * sumYPower2)));
+ }
+
+ return result;
+ }
+
+
+ static class SlopeData {
+ private double sumXPower2;
+ private double sumYPower2;
+ private double sumXY;
+ private double yIntercept; // not used today
+ private Double slope;
+ private Double correlationRate;
+
+ public double getSumXPower2() {
+ return sumXPower2;
+ }
+
+ public void setSumXPower2(double sumXPower2) {
+ this.sumXPower2 = sumXPower2;
+ }
+
+ public double getSumYPower2() {
+ return sumYPower2;
+ }
+
+ public void setSumYPower2(double sumYPower2) {
+ this.sumYPower2 = sumYPower2;
+ }
+
+ public double getSumXY() {
+ return sumXY;
+ }
+
+ public void setSumXY(double sumXY) {
+ this.sumXY = sumXY;
+ }
+
+ public double getYIntercept() {
+ return yIntercept;
+ }
+
+ public void setYIntercept(double yIntercept) {
+ this.yIntercept = yIntercept;
+ }
+
+ public Double getSlope() {
+ return slope;
+ }
+
+ public void setSlope(Double slope) {
+ this.slope = slope;
+ }
+
+ public Double getCorrelationRate() {
+ return correlationRate;
+ }
+
+ public void setCorrelationRate(Double correlationRate) {
+ this.correlationRate = correlationRate;
+ }
+
+ public Integer getLevel() {
+ double hSlope = 0.8;
+ double nSlope = 0.2;
+
+ double vHighCorcoef = 1.0;
+ double modCorcoef = 0.69;
+ Double correlationCoeff = getCorrelationRate();
+ Double slope = getSlope();
+ boolean vHCorCoefPos = (correlationCoeff > modCorcoef) && (correlationCoeff <= vHighCorcoef);
+ boolean vHCorCoefNeg = (correlationCoeff < -modCorcoef) && (correlationCoeff >= -vHighCorcoef);
+
+ if ((vHCorCoefPos || vHCorCoefNeg) && (slope >= hSlope)) {
+ return TENDENCY_BIG_UP;
+
+ } else if ((vHCorCoefPos || vHCorCoefNeg) && (slope <= -hSlope)) {
+ return TENDENCY_BIG_DOWN;
+
+ } else if ((vHCorCoefPos || vHCorCoefNeg) && ((slope >= nSlope) && (slope < hSlope))) {
+ return TENDENCY_UP;
+
+ } else if ((vHCorCoefPos || vHCorCoefNeg) && ((slope <= -nSlope) && (slope > -hSlope))) {
+ return TENDENCY_DOWN;
+
+ } else if ((vHCorCoefPos || vHCorCoefNeg) && ((slope < nSlope) || (slope > -nSlope))) {
+ return TENDENCY_NEUTRAL;
+ } else if (correlationCoeff == 0 && slope == 0 && !vHCorCoefPos && !vHCorCoefNeg) {
+ return TENDENCY_NEUTRAL;
+ }
+ return null;
+ }
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/TendencyDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/TendencyDecorator.java
new file mode 100644
index 00000000000..dcc6c28bd33
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/TendencyDecorator.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.plugins.core.sensors;
+
+import com.google.common.collect.ArrayListMultimap;
+import org.apache.commons.lang.time.DateUtils;
+import org.sonar.api.CoreProperties;
+import org.sonar.api.batch.*;
+import org.sonar.jpa.dao.MeasuresDao;
+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 org.sonar.api.resources.ResourceUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@Phase(name = Phase.Name.POST)
+public class TendencyDecorator implements Decorator {
+
+ public static final String PROP_DAYS_DESCRIPTION = "Number of days the tendency should be calculated on.";
+
+ private MeasuresDao measuresDao;
+ private TimeMachine timeMachine;
+ private TimeMachineQuery query;
+ private TendencyAnalyser analyser;
+
+ public TendencyDecorator(TimeMachine timeMachine, MeasuresDao measuresDao) {
+ this.timeMachine = timeMachine;
+ this.measuresDao = measuresDao;
+ this.analyser = new TendencyAnalyser();
+ }
+
+ protected TendencyDecorator(TimeMachine timeMachine, TimeMachineQuery query, TendencyAnalyser analyser) {
+ this.timeMachine = timeMachine;
+ this.query = query;
+ this.analyser = analyser;
+ }
+
+ protected TimeMachineQuery initQuery(Project project) {
+ int days = project.getConfiguration().getInt(CoreProperties.CORE_TENDENCY_DEPTH_PROPERTY, CoreProperties.CORE_TENDENCY_DEPTH_DEFAULT_VALUE);
+
+ List<Metric> metrics = new ArrayList<Metric>();
+ for (Metric metric : measuresDao.getMetrics()) {
+ if (metric.isNumericType()) {
+ metrics.add(metric);
+ }
+ }
+
+ query = new TimeMachineQuery(null) // resource is set after
+ .setFrom(DateUtils.addDays(project.getAnalysisDate(), -days))
+ .setToCurrentAnalysis(true)
+ .setMetrics(metrics);
+ return query;
+ }
+
+ protected TimeMachineQuery resetQuery(Project project, Resource resource) {
+ if (query == null) {
+ initQuery(project);
+ }
+ query.setResource(resource);
+ return query;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (shouldDecorateResource(resource)) {
+ resetQuery(context.getProject(), resource);
+ List<Object[]> fields = timeMachine.getMeasuresFields(query);
+ ArrayListMultimap<Metric, Double> valuesPerMetric = ArrayListMultimap.create();
+ for (Object[] field : fields) {
+ valuesPerMetric.put((Metric) field[1], (Double) field[2]);
+ }
+
+ for (Metric metric : query.getMetrics()) {
+ Measure measure = context.getMeasure(metric);
+ if (measure != null) {
+ List<Double> values = valuesPerMetric.get(metric);
+ values.add(measure.getValue());
+
+ measure.setTendency(analyser.analyseLevel(valuesPerMetric.get(metric)));
+ context.saveMeasure(measure);
+ }
+ }
+ }
+ }
+
+ private boolean shouldDecorateResource(Resource resource) {
+ return ResourceUtils.isSet(resource) || ResourceUtils.isSpace(resource);
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/UncoveredComplexityDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/UncoveredComplexityDecorator.java
new file mode 100644
index 00000000000..2af5b2f3d08
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/UncoveredComplexityDecorator.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.plugins.core.sensors;
+
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.measures.CoreMetrics;
+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 org.sonar.api.utils.KeyValueFormat;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @deprecated the metric <code>CoreMetrics.UNCOVERED_COMPLEXITY_BY_TESTS</code> is deprecated since v.1.11.
+ * It's replaced by uncovered_line and uncovered_conditions
+ */
+@Deprecated
+public class UncoveredComplexityDecorator implements Decorator {
+
+ @DependsUpon
+ public List<Metric> dependsUponMetrics() {
+ return Arrays.asList(CoreMetrics.COVERAGE, CoreMetrics.COMPLEXITY);
+ }
+
+ @DependedUpon
+ public Metric generatesMetric() {
+ return CoreMetrics.UNCOVERED_COMPLEXITY_BY_TESTS;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ Measure coverage = context.getMeasure(CoreMetrics.COVERAGE);
+ Measure complexity = context.getMeasure(CoreMetrics.COMPLEXITY);
+
+ if (MeasureUtils.haveValues(coverage, complexity)) {
+ double value = complexity.getValue() - (complexity.getValue() * (coverage.getValue() / 100.0));
+ String data = KeyValueFormat.format("CMP", complexity.getValue().intValue(), "COV", coverage.getValue());
+ context.saveMeasure(new Measure(CoreMetrics.UNCOVERED_COMPLEXITY_BY_TESTS, value, data));
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
+
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/UnitTestDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/UnitTestDecorator.java
new file mode 100644
index 00000000000..8bc670aec9d
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/UnitTestDecorator.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.plugins.core.sensors;
+
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.measures.CoreMetrics;
+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 org.sonar.api.resources.ResourceUtils;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+public class UnitTestDecorator implements Decorator {
+
+ @DependedUpon
+ public List<Metric> generatesMetrics() {
+ return Arrays.asList(CoreMetrics.TEST_EXECUTION_TIME, CoreMetrics.TESTS, CoreMetrics.TEST_ERRORS, CoreMetrics.TEST_FAILURES, CoreMetrics.TEST_SUCCESS_DENSITY);
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return !Project.AnalysisType.STATIC.equals(project.getAnalysisType());
+ }
+
+ public boolean shouldDecorateResource(Resource resource) {
+ return ResourceUtils.isUnitTestClass(resource) || !ResourceUtils.isEntity(resource);
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (shouldDecorateResource(resource)) {
+ sumChildren(context, CoreMetrics.TEST_EXECUTION_TIME);
+ sumChildren(context, CoreMetrics.SKIPPED_TESTS);
+ Double tests = sumChildren(context, CoreMetrics.TESTS);
+ Double errors = sumChildren(context, CoreMetrics.TEST_ERRORS);
+ Double failures = sumChildren(context, CoreMetrics.TEST_FAILURES);
+
+ if (isPositive(tests, true) && isPositive(errors, false) && isPositive(failures, false)) {
+ Double errorsAndFailuresRatio = (errors + failures) * 100.0 / tests;
+ context.saveMeasure(CoreMetrics.TEST_SUCCESS_DENSITY, 100.0 - errorsAndFailuresRatio);
+ }
+ }
+ }
+
+ private boolean isPositive(Double d, boolean strict) {
+ return d != null && (strict ? d > 0.0 : d >= 0.0);
+ }
+
+ private Double sumChildren(DecoratorContext jobContext, Metric metric) {
+ Collection<Measure> childrenMeasures = jobContext.getChildrenMeasures(metric);
+ if (childrenMeasures != null && childrenMeasures.size() > 0) {
+ Double sum = 0.0;
+ boolean hasChildrenMeasures = false;
+ for (Measure measure : childrenMeasures) {
+ if (MeasureUtils.hasValue(measure)) {
+ sum += measure.getValue();
+ hasChildrenMeasures = true;
+ }
+ }
+ if (hasChildrenMeasures) {
+ jobContext.saveMeasure(metric, sum);
+ return sum;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/VersionEventsSensor.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/VersionEventsSensor.java
new file mode 100644
index 00000000000..97c572ed9f7
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/VersionEventsSensor.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.plugins.core.sensors;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.time.DateUtils;
+import org.sonar.api.batch.Event;
+import org.sonar.api.batch.Sensor;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.resources.Project;
+
+import java.util.Iterator;
+
+public class VersionEventsSensor implements Sensor {
+
+ private static final String SNAPSHOT_SUFFIX = "-SNAPSHOT";
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ public void analyse(Project project, SensorContext context) {
+ if (StringUtils.isBlank(project.getAnalysisVersion())) {
+ return;
+ }
+ deleteDeprecatedEvents(project, context);
+ context.createEvent(project, project.getAnalysisVersion(), null, Event.CATEGORY_VERSION, null);
+ }
+
+ private void deleteDeprecatedEvents(Project project, SensorContext context) {
+ String version = project.getAnalysisVersion();
+ String snapshotVersionToDelete = (version.endsWith(SNAPSHOT_SUFFIX) ? "" : version + SNAPSHOT_SUFFIX);
+ for (Iterator<Event> it = context.getEvents(project).iterator(); it.hasNext();) {
+ Event event = it.next();
+ if (event.isVersionCategory()) {
+ if (version.equals(event.getName()) || snapshotVersionToDelete.equals(event.getName())) {
+ it.remove();
+ context.deleteEvent(event);
+ event = null;
+ }
+ }
+
+ if (event != null && !event.isLinkedToSnapshot() &&
+ event.getDate() != null && project.getAnalysisDate() != null &&
+ DateUtils.isSameDay(event.getDate(), project.getAnalysisDate())) {
+ it.remove();
+ context.deleteEvent(event);
+ context.createEvent(project, event.getName(), event.getDescription(), event.getCategory(), null);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ViolationsDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ViolationsDecorator.java
new file mode 100644
index 00000000000..85febaa6e96
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ViolationsDecorator.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.plugins.core.sensors;
+
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.Multiset;
+import org.sonar.api.batch.*;
+import org.sonar.api.measures.*;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RulePriority;
+import org.sonar.api.rules.Violation;
+
+import java.util.*;
+
+@DependsUpon(classes = GeneratesViolations.class)
+public class ViolationsDecorator implements Decorator {
+
+ // temporary data for current resource
+ private Multiset<Rule> rules = HashMultiset.create();
+ private Multiset<Integer> categories = HashMultiset.create();
+ private Multiset<RulePriority> priorities = HashMultiset.create();
+ private Map<Rule, RulePriority> ruleToLevel = new HashMap<Rule, RulePriority>();
+ private int total = 0;
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ @DependedUpon
+ public List<Metric> generatesViolationsMetrics() {
+ return Arrays.asList(CoreMetrics.VIOLATIONS,
+ CoreMetrics.BLOCKER_VIOLATIONS, CoreMetrics.CRITICAL_VIOLATIONS, CoreMetrics.MAJOR_VIOLATIONS, CoreMetrics.MINOR_VIOLATIONS, CoreMetrics.INFO_VIOLATIONS,
+ CoreMetrics.USABILITY, CoreMetrics.MAINTAINABILITY, CoreMetrics.EFFICIENCY, CoreMetrics.PORTABILITY, CoreMetrics.RELIABILITY);
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (shouldDecorateResource(resource, context)) {
+ countViolations(context);
+ }
+ }
+
+ private boolean shouldDecorateResource(Resource resource, DecoratorContext context) {
+ return !ResourceUtils.isUnitTestClass(resource) && context.getMeasure(CoreMetrics.VIOLATIONS) == null;
+ }
+
+ private void resetCounters() {
+ rules.clear();
+ categories.clear();
+ priorities.clear();
+ ruleToLevel.clear();
+ total = 0;
+ }
+
+ private void countViolations(DecoratorContext context) {
+ resetCounters();
+ countCurrentResourceViolations(context);
+ saveTotalViolations(context);
+ saveViolationsByPriority(context);
+ saveViolationsByCategory(context);
+ saveViolationsByRule(context);
+ }
+
+ private void saveViolationsByPriority(DecoratorContext context) {
+ for (RulePriority priority : RulePriority.values()) {
+ Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rulePriority(CoreMetrics.VIOLATIONS, priority));
+ double sum = MeasureUtils.sum(true, children) + priorities.count(priority);
+ context.saveMeasure(RuleMeasure.createForPriority(CoreMetrics.VIOLATIONS, priority, sum));
+ context.saveMeasure(new Measure(getMetricForPriority(priority), sum));
+ }
+ }
+
+ private Metric getMetricForPriority(RulePriority priority) {
+ Metric metric = null;
+ if (priority.equals(RulePriority.BLOCKER)) {
+ metric = CoreMetrics.BLOCKER_VIOLATIONS;
+ } else if (priority.equals(RulePriority.CRITICAL)) {
+ metric = CoreMetrics.CRITICAL_VIOLATIONS;
+ } else if (priority.equals(RulePriority.MAJOR)) {
+ metric = CoreMetrics.MAJOR_VIOLATIONS;
+ } else if (priority.equals(RulePriority.MINOR)) {
+ metric = CoreMetrics.MINOR_VIOLATIONS;
+ } else if (priority.equals(RulePriority.INFO)) {
+ metric = CoreMetrics.INFO_VIOLATIONS;
+ }
+ return metric;
+ }
+
+ private void saveViolationsByCategory(DecoratorContext context) {
+ Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.ruleCategories(CoreMetrics.VIOLATIONS));
+ for (Measure childMeasure : children) {
+ RuleMeasure childCategMeasure = (RuleMeasure) childMeasure;
+ categories.add(childCategMeasure.getRuleCategory(), childCategMeasure.getValue().intValue());
+ }
+
+ for (Multiset.Entry<Integer> entry : categories.entrySet()) {
+ context.saveMeasure(RuleMeasure.createForCategory(CoreMetrics.VIOLATIONS, entry.getElement(), (double) entry.getCount()));
+ }
+ }
+
+ private void saveViolationsByRule(DecoratorContext context) {
+ Collection<Measure> children = context.getChildrenMeasures(MeasuresFilters.rules(CoreMetrics.VIOLATIONS));
+ for (Measure childMeasure : children) {
+ RuleMeasure childRuleMeasure = (RuleMeasure) childMeasure;
+ Rule rule = childRuleMeasure.getRule();
+ if (rule != null && MeasureUtils.hasValue(childRuleMeasure)) {
+ rules.add(rule, childRuleMeasure.getValue().intValue());
+ ruleToLevel.put(childRuleMeasure.getRule(), childRuleMeasure.getRulePriority());
+ }
+ }
+ for (Multiset.Entry<Rule> entry : rules.entrySet()) {
+ Rule rule = entry.getElement();
+ RuleMeasure measure = RuleMeasure.createForRule(CoreMetrics.VIOLATIONS, rule, (double) entry.getCount());
+ measure.setRuleCategory(rule.getCategoryId());
+ measure.setRulePriority(ruleToLevel.get(rule));
+ context.saveMeasure(measure);
+ }
+ }
+
+ private void saveTotalViolations(DecoratorContext context) {
+ Collection<Measure> childrenViolations = context.getChildrenMeasures(CoreMetrics.VIOLATIONS);
+ Double sum = MeasureUtils.sum(true, childrenViolations) + total;
+ context.saveMeasure(new Measure(CoreMetrics.VIOLATIONS, sum));
+ }
+
+ private void countCurrentResourceViolations(DecoratorContext context) {
+ List<Violation> violations = context.getViolations();
+ for (Violation violation : violations) {
+ rules.add(violation.getRule());
+ categories.add(violation.getRule().getCategoryId());
+ priorities.add(violation.getPriority());
+ ruleToLevel.put(violation.getRule(), violation.getPriority());
+ }
+ total = violations.size();
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ViolationsDensityDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ViolationsDensityDecorator.java
new file mode 100644
index 00000000000..0f4e9714fd9
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/ViolationsDensityDecorator.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.plugins.core.sensors;
+
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.jpa.dao.RulesDao;
+import org.sonar.api.measures.*;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
+import org.sonar.api.rules.Iso9126RulesCategories;
+import org.sonar.api.rules.RulesCategory;
+
+import java.util.*;
+
+public class ViolationsDensityDecorator implements Decorator {
+
+ private Map<Integer, Metric> metricByCategoryId;
+
+ public ViolationsDensityDecorator(RulesDao rulesDao) {
+ metricByCategoryId = new HashMap<Integer, Metric>();
+ for (RulesCategory category : rulesDao.getCategories()) {
+ if (category.equals(Iso9126RulesCategories.EFFICIENCY)) {
+ metricByCategoryId.put(category.getId(), CoreMetrics.EFFICIENCY);
+
+ } else if (category.equals(Iso9126RulesCategories.MAINTAINABILITY)) {
+ metricByCategoryId.put(category.getId(), CoreMetrics.MAINTAINABILITY);
+
+ } else if (category.equals(Iso9126RulesCategories.PORTABILITY)) {
+ metricByCategoryId.put(category.getId(), CoreMetrics.PORTABILITY);
+
+ } else if (category.equals(Iso9126RulesCategories.RELIABILITY)) {
+ metricByCategoryId.put(category.getId(), CoreMetrics.RELIABILITY);
+
+ } else if (category.equals(Iso9126RulesCategories.USABILITY)) {
+ metricByCategoryId.put(category.getId(), CoreMetrics.USABILITY);
+ }
+ }
+ }
+
+ protected ViolationsDensityDecorator(Map<Integer, Metric> metricByCategoryId) {
+ this.metricByCategoryId = metricByCategoryId;
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ @DependsUpon
+ public List<Metric> dependsUponWeightedViolationsAndNcloc() {
+ return Arrays.asList(CoreMetrics.WEIGHTED_VIOLATIONS, CoreMetrics.NCLOC);
+ }
+
+ @DependedUpon
+ public Metric generatesViolationsDensity() {
+ return CoreMetrics.VIOLATIONS_DENSITY;
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ if (shouldDecorateResource(context)) {
+ decorateDensity(resource, context);
+ }
+ }
+
+ protected boolean shouldDecorateResource(DecoratorContext context) {
+ return context.getMeasure(CoreMetrics.VIOLATIONS_DENSITY) == null;
+ }
+
+ private void decorateDensity(Resource resource, DecoratorContext context) {
+ Measure ncloc = context.getMeasure(CoreMetrics.NCLOC);
+ if (MeasureUtils.hasValue(ncloc) && ncloc.getValue() > 0.0) {
+ saveDensity(context, ncloc.getValue().intValue());
+ if (ResourceUtils.isSpace(resource) || ResourceUtils.isSet(resource)) {
+ saveDensityByCategory(context, ncloc.getValue().intValue());
+ }
+ }
+ }
+
+ private void saveDensity(DecoratorContext context, int ncloc) {
+ Measure debt = context.getMeasure(CoreMetrics.WEIGHTED_VIOLATIONS);
+ Integer debtValue = 0;
+ if (MeasureUtils.hasValue(debt)) {
+ debtValue = debt.getValue().intValue();
+ }
+ double density = calculate(debtValue, ncloc);
+ context.saveMeasure(CoreMetrics.VIOLATIONS_DENSITY, density);
+ }
+
+ protected static double calculate(int debt, int ncloc) {
+ double rci = (1.0 - ((double) debt / (double) ncloc)) * 100.0;
+ rci = Math.max(rci, 0.0);
+ return rci;
+ }
+
+ private void saveDensityByCategory(DecoratorContext context, int ncloc) {
+ Collection<RuleMeasure> categDebts = context.getMeasures(MeasuresFilters.ruleCategories(CoreMetrics.WEIGHTED_VIOLATIONS));
+ Set<Integer> categIdsDone = new HashSet<Integer>();
+ if (categDebts != null) {
+ for (RuleMeasure categDebt : categDebts) {
+ if (MeasureUtils.hasValue(categDebt)) {
+ double density = calculate(categDebt.getValue().intValue(), ncloc);
+ context.saveMeasure(RuleMeasure.createForCategory(
+ CoreMetrics.VIOLATIONS_DENSITY, categDebt.getRuleCategory(), density));
+ context.saveMeasure(metricByCategoryId.get(categDebt.getRuleCategory()), density);
+ categIdsDone.add(categDebt.getRuleCategory());
+ }
+ }
+ }
+ for (Map.Entry<Integer, Metric> entry : metricByCategoryId.entrySet()) {
+ if (!categIdsDone.contains(entry.getKey())) {
+ context.saveMeasure(entry.getValue(), 100.0);
+ }
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName();
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/WeightedViolationsDecorator.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/WeightedViolationsDecorator.java
new file mode 100644
index 00000000000..36c993e4d7d
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/sensors/WeightedViolationsDecorator.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.plugins.core.sensors;
+
+import com.google.common.collect.Multiset;
+import com.google.common.collect.TreeMultiset;
+import org.apache.commons.lang.ObjectUtils;
+import org.sonar.api.batch.Decorator;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.DependedUpon;
+import org.sonar.api.batch.DependsUpon;
+import org.sonar.api.measures.*;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.RulePriority;
+import org.sonar.api.rules.RuleUtils;
+import org.sonar.api.utils.KeyValueFormat;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class WeightedViolationsDecorator implements Decorator {
+
+ private Map<RulePriority, Integer> weights;
+
+
+ @DependsUpon
+ public Metric dependsUponViolations() {
+ return CoreMetrics.VIOLATIONS;
+ }
+
+ @DependedUpon
+ public Metric generatesWeightedViolations() {
+ return CoreMetrics.WEIGHTED_VIOLATIONS;
+ }
+
+ public WeightedViolationsDecorator() {
+ }
+
+ /**
+ * for unit tests
+ */
+ protected WeightedViolationsDecorator(Map<RulePriority, Integer> weights) {
+ this.weights = weights;
+ }
+
+ private void loadWeights(DecoratorContext context) {
+ if (weights == null && context != null) {
+ weights = RuleUtils.getPriorityWeights(context.getProject().getConfiguration());
+ }
+ }
+
+ public boolean shouldExecuteOnProject(Project project) {
+ return true;
+ }
+
+ public void decorate(Resource resource, DecoratorContext context) {
+ loadWeights(context);
+
+ double debt = 0.0;
+ Multiset<RulePriority> violationsByPriority = TreeMultiset.create();
+ Map<Integer, Double> categoryDebt = new HashMap<Integer, Double>();
+
+ for (RuleMeasure violations : context.getMeasures(MeasuresFilters.rules(CoreMetrics.VIOLATIONS))) {
+ if (MeasureUtils.hasValue(violations)) {
+ violationsByPriority.add(violations.getRulePriority(), violations.getValue().intValue());
+ double add = (int) weights.get(violations.getRulePriority()) * violations.getValue();
+ debt += add;
+
+ Double categoryVal = (Double) ObjectUtils.defaultIfNull(categoryDebt.get(violations.getRuleCategory()), 0.0);
+ categoryDebt.put(violations.getRuleCategory(), categoryVal + add);
+ }
+ }
+
+ Measure debtMeasure = new Measure(CoreMetrics.WEIGHTED_VIOLATIONS, debt, KeyValueFormat.format(violationsByPriority));
+ saveMeasure(context, debtMeasure);
+
+ for (Map.Entry<Integer, Double> entry : categoryDebt.entrySet()) {
+ RuleMeasure categDebt = RuleMeasure.createForCategory(CoreMetrics.WEIGHTED_VIOLATIONS, entry.getKey(), entry.getValue());
+ categDebt.setPersistenceMode(PersistenceMode.MEMORY);
+ saveMeasure(context, categDebt);
+ }
+ }
+
+ private void saveMeasure(DecoratorContext context, Measure debtMeasure) {
+ if (debtMeasure.getValue() > 0.0) {
+ context.saveMeasure(debtMeasure);
+ }
+ }
+
+ protected Map<RulePriority, Integer> getWeights() {
+ return weights;
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/testdetailsviewer/TestsViewerDefinition.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/testdetailsviewer/TestsViewerDefinition.java
new file mode 100644
index 00000000000..0cca72a7d7b
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/testdetailsviewer/TestsViewerDefinition.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.plugins.core.testdetailsviewer;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.*;
+import org.sonar.plugins.core.testdetailsviewer.client.TestsViewer;
+
+@ResourceQualifier(Resource.QUALIFIER_UNIT_TEST_CLASS)
+@NavigationSection(NavigationSection.RESOURCE_TAB)
+@DefaultTab(metrics={CoreMetrics.TESTS_KEY, CoreMetrics.TEST_EXECUTION_TIME_KEY, CoreMetrics.TEST_SUCCESS_DENSITY_KEY, CoreMetrics.TEST_FAILURES_KEY, CoreMetrics.TEST_ERRORS_KEY, CoreMetrics.SKIPPED_TESTS_KEY})
+@UserRole(UserRole.CODEVIEWER)
+public class TestsViewerDefinition extends GwtPage {
+
+ public String getTitle() {
+ return "Tests";
+ }
+
+ public String getGwtId() {
+ return TestsViewer.GWT_ID;
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/testdetailsviewer/client/TestsPanel.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/testdetailsviewer/client/TestsPanel.java
new file mode 100644
index 00000000000..ace2bb747a6
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/testdetailsviewer/client/TestsPanel.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.plugins.core.testdetailsviewer.client;
+
+import com.google.gwt.gen2.table.override.client.FlexTable;
+import com.google.gwt.gen2.table.override.client.FlexTable.FlexCellFormatter;
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.ui.*;
+import com.google.gwt.xml.client.Document;
+import com.google.gwt.xml.client.Element;
+import com.google.gwt.xml.client.NodeList;
+import com.google.gwt.xml.client.XMLParser;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.ExpandCollapseLink;
+import org.sonar.gwt.ui.Loading;
+import org.sonar.wsclient.gwt.AbstractCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+public class TestsPanel extends Composite {
+
+ private final Panel panel;
+ private Loading loading;
+
+ public TestsPanel(Resource resource) {
+ panel = new VerticalPanel();
+ loading = new Loading();
+ panel.add(loading);
+ initWidget(panel);
+ setStyleName("gwt-TestDetailsPanel");
+ getElement().setId("gwt-TestDetailsPanel");
+ loadData(resource);
+ }
+
+ private void loadData(Resource resource) {
+ ResourceQuery query = ResourceQuery.createForResource(resource, Metrics.TEST_DATA);
+ Sonar.getInstance().find(query, new TestDetailsMeasureHandler());
+ }
+
+ private class TestDetailsMeasureHandler extends AbstractCallback<Resource> {
+
+ public TestDetailsMeasureHandler() {
+ super(loading);
+ }
+
+ @Override
+ protected void doOnResponse(Resource resource) {
+ loading.removeFromParent();
+ if (resource != null) {
+ Measure measure = resource.getMeasure(Metrics.TEST_DATA);
+ processTestDetails(measure.getData());
+ }
+ }
+
+ private void processTestDetails(String testXMLData) {
+ Document parsed = XMLParser.parse(testXMLData);
+ NodeList testcasesXML = parsed.getElementsByTagName("testcase");
+
+ FlexTable table = new FlexTable();
+ table.setStylePrimaryName("detailsTable");
+ table.setText(0, 0, "");
+ table.setText(0, 1, "");
+ table.setText(0, 2, "Duration");
+ table.setText(0, 3, "Unit test name");
+ table.getCellFormatter().getElement(0, 1).setId("iCol");
+ table.getCellFormatter().getElement(0, 2).setId("dCol");
+ setRowStyle(0, table, "header", false);
+
+ int rowCounter = 1;
+ for (int i = 0; i < testcasesXML.getLength(); i++) {
+ Element testcaseXML = (Element) testcasesXML.item(i);
+ String time = testcaseXML.getAttribute("time");
+ String name = testcaseXML.getAttribute("name");
+ String status = testcaseXML.getAttribute("status");
+ Element error = getFirstElement("error", testcaseXML);
+ Element failure = getFirstElement("failure", testcaseXML);
+ Element stackTrace = status.equals("error") ? error : failure;
+ renderTestDetails(rowCounter, i, status, stackTrace, name, time, table);
+ rowCounter += 2;
+ }
+ panel.add(table);
+ }
+
+ private Element getFirstElement(String elementName, Element node) {
+ NodeList elements = node.getElementsByTagName(elementName);
+ return elements.getLength() > 0 ? (Element) elements.item(0) : null;
+ }
+
+ private void renderTestDetails(int row, int testCounter, String testCaseStatus, Element stackTrace, String name, String timeMS, FlexTable table) {
+
+ HTML icon = new HTML("&nbsp;");
+ icon.setStyleName(testCaseStatus);
+ table.setWidget(row, 1, icon);
+ table.setText(row, 2, timeMS + " ms");
+
+ table.setText(row, 3, name);
+ String style = (testCounter % 2 == 0) ? "odd" : "even";
+ setRowStyle(row, table, style, false);
+
+ if (stackTrace != null) {
+ Panel stackPanel = new SimplePanel();
+ stackPanel.setStyleName("stackPanel");
+ stackPanel.getElement().setId("stack-panel" + name);
+ stackPanel.setVisible(false);
+ fillStackPanel(stackPanel, stackTrace);
+
+ FlexCellFormatter frmt = (FlexCellFormatter) table.getCellFormatter();
+ frmt.setColSpan(row + 1, 1, 3);
+ table.setWidget(row + 1, 1, stackPanel);
+ table.setWidget(row, 0, new ExpandCollapseLink(stackPanel));
+ table.getCellFormatter().getElement(row, 0).setId("expandCollapseCol");
+ setRowStyle(row + 1, table, style, true);
+ }
+ }
+
+ private void setRowStyle(int row, FlexTable table, String style, boolean isPanelRow) {
+ table.getCellFormatter().setStyleName(row, 0, style);
+ table.getCellFormatter().setStyleName(row, 1, style);
+ if (!isPanelRow) {
+ table.getCellFormatter().setStyleName(row, 2, style);
+ table.getCellFormatter().setStyleName(row, 3, style);
+ }
+ table.getCellFormatter().getElement(row, 0).setId("noLinkExpandCollapseCol");
+ }
+ }
+
+ private void fillStackPanel(Panel p, Element stackElement) {
+ String message = stackElement.getAttribute("message");
+ if (message.length() > 0) {
+ p.getElement().setInnerHTML(escapeHtml(message) + "<br/>" + stackLineBreaks(stackElement.getFirstChild().getNodeValue()));
+ } else {
+ p.getElement().setInnerHTML(stackLineBreaks(stackElement.getFirstChild().getNodeValue()));
+ }
+ }
+
+ private String escapeHtml(String maybeHtml) {
+ com.google.gwt.dom.client.Element div = DOM.createDiv();
+ div.setInnerText(maybeHtml);
+ return div.getInnerHTML();
+ }
+
+ private String stackLineBreaks(String s) {
+ StringBuilder stack = new StringBuilder(256);
+ for (String el : s.split("\n")) {
+ stack.append(el.trim()).append("<br/>");
+ }
+ return stack.toString();
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/testdetailsviewer/client/TestsViewer.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/testdetailsviewer/client/TestsViewer.java
new file mode 100644
index 00000000000..e6dfc2545e1
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/testdetailsviewer/client/TestsViewer.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.plugins.core.testdetailsviewer.client;
+
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.HorizontalPanel;
+import com.google.gwt.user.client.ui.Widget;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.Page;
+import org.sonar.gwt.ui.ViewerHeader;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+
+public class TestsViewer extends Page {
+
+ public static final String GWT_ID = "org.sonar.plugins.core.testdetailsviewer.TestsViewer";
+
+ @Override
+ protected Widget doOnResourceLoad(Resource resource) {
+ FlowPanel flowPanel = new FlowPanel();
+ flowPanel.add(new UnitTestsHeader(resource));
+ flowPanel.add(new TestsPanel(resource));
+ return flowPanel;
+ }
+
+ private static class UnitTestsHeader extends ViewerHeader {
+ public UnitTestsHeader(Resource resource) {
+
+ super(resource, new String[]{
+ Metrics.TEST_ERRORS,
+ Metrics.TEST_FAILURES,
+ Metrics.TEST_SUCCESS_DENSITY,
+ Metrics.TESTS,
+ Metrics.SKIPPED_TESTS,
+ Metrics.TEST_EXECUTION_TIME}
+ );
+ }
+
+ @Override
+ protected void display(FlowPanel header, Resource resource) {
+ HorizontalPanel panel = new HorizontalPanel();
+ header.add(panel);
+
+ Measure measure = resource.getMeasure(Metrics.TEST_SUCCESS_DENSITY);
+ if (measure == null) {
+ addBigCell(panel, "100%"); // best value
+ } else {
+ addBigCell(panel, measure.getFormattedValue());
+ }
+
+ String skippedHtml = "";
+ Measure skipped = resource.getMeasure(Metrics.SKIPPED_TESTS);
+ if (skipped != null && skipped.getValue() > 0.0) {
+ skippedHtml += " (+" + skipped.getFormattedValue() + " skipped)";
+ }
+ addCell(panel,
+ "Tests: ",
+ resource.getMeasureFormattedValue(Metrics.TESTS, "-") + skippedHtml);
+
+ addCell(panel,
+ "Failures/Errors: ",
+ resource.getMeasureFormattedValue(Metrics.TEST_FAILURES, "0") + "/" + resource.getMeasureFormattedValue(Metrics.TEST_ERRORS, "0"));
+
+ addCell(panel,
+ "Duration: ",
+ resource.getMeasureFormattedValue(Metrics.TEST_EXECUTION_TIME, "-"));
+ }
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/GwtPageSelector.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/GwtPageSelector.java
new file mode 100644
index 00000000000..ccebcbf588c
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/GwtPageSelector.java
@@ -0,0 +1,30 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.ui.pageselector;
+
+import org.sonar.api.web.GwtExtension;
+import org.sonar.plugins.core.ui.pageselector.client.PageSelector;
+
+public class GwtPageSelector implements GwtExtension {
+
+ public String getGwtId() {
+ return PageSelector.GWT_ID;
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/client/I18nConstants.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/client/I18nConstants.java
new file mode 100644
index 00000000000..29e5d451445
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/client/I18nConstants.java
@@ -0,0 +1,30 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.ui.pageselector.client;
+
+import com.google.gwt.core.client.GWT;
+
+public interface I18nConstants extends com.google.gwt.i18n.client.Constants {
+
+ static I18nConstants INSTANCE = GWT.create(I18nConstants.class);
+
+ @DefaultStringValue("New window")
+ String newWindow();
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PageDef.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PageDef.java
new file mode 100644
index 00000000000..fdaa9dbdeda
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PageDef.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.plugins.core.ui.pageselector.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+public class PageDef extends JavaScriptObject {
+ // Overlay types always have protected, zero-arg ctors
+
+ protected PageDef() {
+ }
+
+ public final native boolean isGwt() /*-{ return this.gwt; }-*/;
+
+ public final native boolean isDefaultTab() /*-{ return this.d; }-*/;
+
+ public final native String getId() /*-{ return this.id; }-*/;
+
+ public final native String getUrl() /*-{ return this.url; }-*/;
+
+ public final native String getName() /*-{ return this.name; }-*/;
+
+ public final native StringArray getMetrics() /*-{ return this.m || []; }-*/;
+
+ public final native StringArray getLanguages() /*-{ return this.l || []; }-*/;
+
+ public final native StringArray getScopes() /*-{ return this.s || []; }-*/;
+
+ public final native StringArray getQualifiers() /*-{ return this.q || []; }-*/;
+
+ public final boolean acceptLanguage(String language) {
+ return hasValue(getLanguages(), language);
+ }
+
+ public final boolean acceptScope(String scope) {
+ return hasValue(getScopes(), scope);
+ }
+
+ public final boolean acceptQualifier(String qualifier) {
+ return hasValue(getQualifiers(), qualifier);
+ }
+
+ public final boolean acceptMetric(String metric) {
+ StringArray metrics = getMetrics();
+ for (int index = 0; index < metrics.length(); index++) {
+ if (metric.equals(metrics.get(index))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ private boolean hasValue(StringArray array, String value) {
+ if (array == null || array.length() == 0) {
+ return true;
+ }
+ if (value != null) {
+ for (int index = 0; index < array.length(); index++) {
+ if (value.equals(array.get(index))) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+}
+
+class StringArray extends JavaScriptObject {
+ protected StringArray() {
+ }
+
+ public final native int length() /*-{ return this.length; }-*/;
+
+ public final native String get(int i) /*-{ return this[i]; }-*/;
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PagePanel.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PagePanel.java
new file mode 100644
index 00000000000..6c28f8c4d6c
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PagePanel.java
@@ -0,0 +1,156 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.ui.pageselector.client;
+
+import com.google.gwt.http.client.*;
+import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.SimplePanel;
+import org.sonar.api.web.gwt.client.Utils;
+import org.sonar.gwt.Configuration;
+import org.sonar.gwt.Links;
+import org.sonar.gwt.ui.Loading;
+
+public class PagePanel extends SimplePanel {
+
+ private PageDef def;
+ private String rootPanelId;
+ private String currentResourceId = null;
+
+ public PagePanel(PageDef def) {
+ this.def = def;
+ rootPanelId = "gwtpage-" + def.getId();
+ add(new HTML("<div id=\"" + rootPanelId + "\"> </div>"));
+ }
+
+ public void display() {
+ String resourceId = Configuration.getResourceId();
+ if (resourceId != null && !resourceId.equals(currentResourceId)) {
+ currentResourceId = resourceId;
+ if (def.isGwt()) {
+ loadGwt(Links.baseUrl(), Configuration.getSonarVersion(), def.getId());
+ } else {
+ loadEmbeddedPage(resourceId);
+ }
+ }
+ }
+
+ private native void loadGwt(final String serverUrl, final String sonarVersion, final String gwtId) /*-{
+ if ($wnd.modules[gwtId]!=null) {
+ $wnd.modules[gwtId]();
+ return;
+ }
+
+ // Create the script tag to be used for importing the GWT script loader.
+ var script = $doc.createElement('script');
+ script.type = 'text/javascript';
+ script.src = serverUrl + '/deploy/gwt/' + gwtId + '/' + gwtId + '.nocache.js?' + sonarVersion;
+
+ // The default GWT script loader calls document.write() twice which prevents loading scripts
+ // on demand, after the document has been loaded. To overcome this we have to overwrite the document.write()
+ // method before the GWT script loader is executed and restore it after.
+ // NOTE: The GWT script loader uses document.write() to compute the URL from where it is loaded.
+ var counter = 0;
+ var limit = 2;
+ var oldWrite = $doc.write;
+ var newWrite = function(html) {
+ if (counter < limit) {
+ counter++;
+ // Fail silently if the script element hasn't been attached to the document.
+ if (!script.parentNode) {
+ return;
+ }
+ // Create a DIV and put the HTML inside.
+ var div = $doc.createElement('div');
+ // We have to replace all the script tags because otherwise IE drops them.
+ div.innerHTML = html.replace(/<script\b([\s\S]*?)<\/script>/gi, "<pre script=\"script\"$1</pre>");
+ // Move DIV contents after the GWT script loader.
+ var nextSibling = script.nextSibling;
+ while(div.firstChild) {
+ var child = div.firstChild;
+ // Recover the script tags.
+ if (child.nodeName.toLowerCase() == 'pre' && child.getAttribute('script') == 'script') {
+ var pre = child;
+ pre.removeAttribute('script');
+ // Create the script tag.
+ child = $doc.createElement('script');
+ // Copy all the attributes.
+ for (var i = 0; i < pre.attributes.length; i++) {
+ var attrNode = pre.attributes[i];
+ // In case of IE we have to copy only the specified attributes.
+ if (typeof attrNode.specified == 'undefined'
+ || (typeof attrNode.specified == 'boolean' && attrNode.specified)) {
+ child.setAttribute(attrNode.nodeName, attrNode.nodeValue);
+ }
+ }
+ // Copy the script text.
+ child.text = typeof pre.innerText == 'undefined' ? pre.textContent : pre.innerText;
+ // Don't forget to remove the placeholder.
+ div.removeChild(pre);
+ }
+ if (nextSibling) {
+ script.parentNode.insertBefore(child, nextSibling);
+ } else {
+ script.parentNode.appendChild(child);
+ }
+ }
+ }
+ if (counter >= limit) {
+ $doc.write = oldWrite;
+ oldWrite = undefined;
+ script = undefined;
+ counter = undefined;
+ }
+ };
+
+ // Append the script tag to the head.
+ var heads = $doc.getElementsByTagName('head');
+ if (heads.length > 0) {
+ $doc.write = newWrite;
+ heads[0].appendChild(script);
+ }
+ }-*/;
+
+ private void loadEmbeddedPage(String resourceId) {
+ final RootPanel panel = RootPanel.get(rootPanelId);
+ panel.add(new Loading());
+ String url = def.getUrl();
+ if (url == null) {
+ url = "/plugins/resource/" + resourceId + "?page=" + def.getId() + "&layout=false&hd=false";
+ } else {
+ url += resourceId;
+ }
+ RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, URL.encode(Links.baseUrl() + url));
+ try {
+ builder.sendRequest(null, new RequestCallback() {
+ public void onError(Request request, Throwable exception) {
+ Utils.showError("Can not load the page " + request.toString());
+ }
+
+ public void onResponseReceived(Request request, Response response) {
+ panel.clear();
+ panel.add(new HTML(response.getText()));
+ }
+ });
+ } catch (RequestException e) {
+ Utils.showError("Can not connect to server: " + url);
+ }
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PageSelector.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PageSelector.java
new file mode 100644
index 00000000000..76b6226fc58
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/ui/pageselector/client/PageSelector.java
@@ -0,0 +1,224 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.ui.pageselector.client;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.logical.shared.SelectionEvent;
+import com.google.gwt.event.logical.shared.SelectionHandler;
+import com.google.gwt.user.client.ui.*;
+import org.sonar.gwt.Configuration;
+import org.sonar.gwt.Links;
+import org.sonar.gwt.Utils;
+import org.sonar.gwt.ui.Icons;
+import org.sonar.gwt.ui.Loading;
+import org.sonar.wsclient.gwt.Callback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PageSelector implements EntryPoint {
+
+ public static final String GWT_ID = "org.sonar.plugins.core.ui.pageselector.PageSelector";
+ public static final String HTML_ROOT_ID = "pageselector";
+
+ private VerticalPanel container = null;
+ private String currentResourceId = null;
+ private PageDefs pageDefs = null;
+
+ public void onModuleLoad() {
+ pageDefs = loadPageDefs();
+ exportNativeJavascript(this);
+ if (Configuration.getResourceId() != null) {
+ selectResource(Configuration.getResourceId());
+ }
+ }
+
+ private VerticalPanel createContainer() {
+ if (container == null) {
+ container = new VerticalPanel();
+ container.getElement().setId("rvs");
+ RootPanel.get(HTML_ROOT_ID).add(container);
+ }
+ return container;
+ }
+
+ public static native void exportNativeJavascript(Object obj) /*-{
+ $wnd.sr=function(resourceIdOrKey) {
+ obj.@org.sonar.plugins.core.ui.pageselector.client.PageSelector::selectResource(Ljava/lang/String;)(resourceIdOrKey);
+ };
+ }-*/;
+
+ public void selectResource(final String resourceIdOrKey) {
+ createContainer().add(new Loading());
+ currentResourceId = resourceIdOrKey;
+ Sonar.getInstance().find(new ResourceQuery(resourceIdOrKey), new Callback<Resource>() {
+
+ public void onResponse(Resource resource, JavaScriptObject json) {
+ if (resourceIdOrKey != null && resourceIdOrKey.equals(currentResourceId)) {
+ if (resource == null) {
+ displayResourceNotFound();
+ } else {
+ saveResource(resource.getId().toString(), json);
+ displayResource(resource);
+ }
+ } // else too late, user has selected another resource
+ }
+
+ public void onTimeout() {
+ Utils.showError("Can not load data (timeout)");
+ }
+
+ public void onError(int errorCode, String errorMessage) {
+ Utils.showError("Can not load data: error " + errorCode + ", message: " + errorMessage);
+ }
+ });
+ }
+
+ private void displayResource(final Resource resource) {
+ List<PageDef> pages = selectPages(resource);
+
+ PageDef selectedPage = selectPage(pages);
+
+ Title title = new Title(resource);
+ final TabPanel tabs = new TabPanel();
+ tabs.setWidth("100%");
+
+ int selectedTabIndex = -1;
+ for (int tabIndex = 0; tabIndex < pages.size(); tabIndex++) {
+ PageDef page = pages.get(tabIndex);
+ tabs.add(new PagePanel(page), page.getName());
+ if (page == selectedPage) {
+ selectedTabIndex = tabIndex;
+ }
+ }
+
+ container.clear(); // remove the loading icon
+ container.add(title);
+ container.add(tabs);
+
+ tabs.addSelectionHandler(new SelectionHandler<Integer>() {
+ public void onSelection(SelectionEvent<Integer> tabId) {
+ ((PagePanel) tabs.getWidget(tabId.getSelectedItem())).display();
+ }
+ });
+
+ if (selectedTabIndex > -1) {
+ tabs.selectTab(selectedTabIndex);
+ }
+ }
+
+ private PageDef selectPage(List<PageDef> pages) {
+ String pageId = Configuration.getRequestParameter("page");
+ if (pageId != null) {
+ for (PageDef page : pages) {
+ if (pageId.equals(page.getId())) {
+ return page;
+ }
+ }
+ }
+ String metric = Configuration.getParameter("metric");
+ if (metric != null) {
+ for (PageDef page : pages) {
+ if (page.acceptMetric(metric)) {
+ return page;
+ }
+ }
+ }
+
+ for (PageDef page : pages) {
+ if (page.isDefaultTab()) {
+ return page;
+ }
+ }
+
+ return null;
+ }
+
+ private native void saveResource(String resourceId, JavaScriptObject json) /*-{
+ $wnd.config['resource_key']=resourceId;
+ $wnd.config['resource']=json;
+ }-*/;
+
+
+ /**
+ * Never return null.
+ */
+ private List<PageDef> selectPages(final Resource resource) {
+ List<PageDef> pages = new ArrayList<PageDef>();
+ for (int index = 0; index < pageDefs.length(); index++) {
+ PageDef page = pageDefs.get(index);
+ if (page.acceptLanguage(resource.getLanguage()) &&
+ page.acceptQualifier(resource.getQualifier()) &&
+ page.acceptScope(resource.getScope())) {
+ pages.add(page);
+ }
+ }
+ return pages;
+ }
+
+ private void displayResourceNotFound() {
+ container.clear(); // remove the loading icon
+ }
+
+
+ static class Title extends Composite {
+ Title(final Resource resource) {
+ Grid grid = new Grid(1, 2);
+ grid.getElement().setId("rvstitle");
+ grid.setHTML(0, 0, Icons.forQualifier(resource.getQualifier()).getHTML() + " <span class='name'>" + resource.getName(true) + "</span>");
+
+ if (!"true".equals(Configuration.getParameter("popup"))) {
+ Hyperlink newWindow = new Hyperlink();
+ newWindow.setText(I18nConstants.INSTANCE.newWindow());
+ newWindow.setStyleName("command");
+ newWindow.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent clickEvent) {
+ Links.openMeasurePopup(resource.getKey(), Configuration.getParameter("metric"));
+ }
+ });
+ grid.setWidget(0, 1, newWindow);
+ }
+ grid.getColumnFormatter().setStyleName(1, "right");
+ initWidget(grid);
+ }
+ }
+
+
+ private native PageDefs loadPageDefs() /*-{
+ return $wnd.pages;
+ }-*/;
+
+ // An overlay type
+
+ static class PageDefs extends JavaScriptObject {
+ protected PageDefs() {
+ }
+
+ public final native int length() /*-{ return this.length; }-*/;
+
+ public final native PageDef get(int i) /*-{ return this[i]; }-*/;
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/violationsviewer/ViolationsViewerDefinition.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/violationsviewer/ViolationsViewerDefinition.java
new file mode 100644
index 00000000000..6b56ecc510c
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/violationsviewer/ViolationsViewerDefinition.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.plugins.core.violationsviewer;
+
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.web.*;
+import org.sonar.plugins.core.violationsviewer.client.ViolationsViewer;
+
+@NavigationSection(NavigationSection.RESOURCE_TAB)
+@DefaultTab(metrics={CoreMetrics.VIOLATIONS_DENSITY_KEY, CoreMetrics.WEIGHTED_VIOLATIONS_KEY, CoreMetrics.VIOLATIONS_KEY, CoreMetrics.BLOCKER_VIOLATIONS_KEY, CoreMetrics.CRITICAL_VIOLATIONS_KEY, CoreMetrics.MAJOR_VIOLATIONS_KEY, CoreMetrics.MINOR_VIOLATIONS_KEY, CoreMetrics.INFO_VIOLATIONS_KEY})
+@ResourceQualifier({Resource.QUALIFIER_CLASS,Resource.QUALIFIER_FILE})
+@UserRole(UserRole.CODEVIEWER)
+public class ViolationsViewerDefinition extends GwtPage {
+
+ public String getTitle() {
+ return "Violations";
+ }
+
+ public String getGwtId() {
+ return ViolationsViewer.GWT_ID;
+ }
+}
+
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/violationsviewer/client/I18nConstants.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/violationsviewer/client/I18nConstants.java
new file mode 100644
index 00000000000..5a1767f0eb0
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/violationsviewer/client/I18nConstants.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.plugins.core.violationsviewer.client;
+
+import com.google.gwt.core.client.GWT;
+
+public interface I18nConstants extends com.google.gwt.i18n.client.Constants {
+
+ static I18nConstants INSTANCE = GWT.create(I18nConstants.class);
+
+ @DefaultStringValue("Filter:")
+ String filter();
+
+ @DefaultStringValue("No filters")
+ String noFilters();
+
+ @DefaultStringValue("Expand:")
+ String expand();
+
+ @DefaultStringValue("Loading...")
+ String loading();
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/violationsviewer/client/ViolationsPanel.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/violationsviewer/client/ViolationsPanel.java
new file mode 100644
index 00000000000..5554154532c
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/violationsviewer/client/ViolationsPanel.java
@@ -0,0 +1,155 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.violationsviewer.client;
+
+import org.sonar.gwt.Links;
+import org.sonar.gwt.Utils;
+import org.sonar.gwt.ui.Icons;
+import org.sonar.gwt.ui.SourcePanel;
+import org.sonar.wsclient.gwt.AbstractListCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.Violation;
+import org.sonar.wsclient.services.ViolationQuery;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ViolationsPanel extends SourcePanel {
+ private boolean expand = false;
+ private List<Violation> violations;
+ private Map<Integer, List<Violation>> filteredViolationsByLine = new HashMap<Integer, List<Violation>>();
+
+ public ViolationsPanel(Resource resource, String filter) {
+ super(resource);
+ loadViolations(resource, filter);
+ }
+
+ protected void loadViolations(final Resource resource, final String filter) {
+ Sonar.getInstance().findAll(ViolationQuery.createForResource(resource), new AbstractListCallback<Violation>() {
+
+ @Override
+ protected void doOnResponse(List<Violation> violations) {
+ ViolationsPanel.this.violations = violations;
+ filter(filter);
+ setStarted();
+ }
+ });
+ }
+
+ public boolean isExpand() {
+ return expand;
+ }
+
+ public void setExpand(boolean expand) {
+ this.expand = expand;
+ }
+
+ public void filter(String filter) {
+ filteredViolationsByLine.clear();
+ for (Violation violation : violations) {
+ if (filter == null || filter.equals("") || violation.getRuleKey().equals(filter) || violation.getPriority().equals(filter)) {
+ Integer line=0;
+ if (violation.getLine()!=null) {
+ line = violation.getLine();
+ }
+ List<Violation> lineViolations = filteredViolationsByLine.get(line);
+ if (lineViolations == null) {
+ lineViolations = new ArrayList<Violation>();
+ filteredViolationsByLine.put(line, lineViolations);
+ }
+ lineViolations.add(violation);
+ }
+ }
+ }
+
+ @Override
+ public boolean shouldDecorateLine(int index) {
+ if (expand) {
+ return true;
+ }
+ for (int i = index - 5; i < index + 5; i++) {
+ if (hasViolations(i)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected List<Row> decorateLine(int index, String source) {
+ List<Row> rows = new ArrayList<Row>();
+ List<Violation> lineViolations = filteredViolationsByLine.get(index);
+ boolean hasViolations = lineViolations != null && !lineViolations.isEmpty();
+
+ if (index > 0) {
+ String style = (hasViolations ? "red" : "");
+ Row row = new Row().setLineIndex(index, style).unsetValue().setSource(source, style);
+ rows.add(row);
+ }
+
+ if (hasViolations) {
+ for (Violation violation : lineViolations) {
+ rows.add(new ViolationRow(violation));
+ }
+ }
+ return rows;
+ }
+
+
+ public static class ViolationRow extends Row {
+ private Violation violation;
+
+ public ViolationRow(Violation violation) {
+ this.violation = violation;
+ }
+
+ @Override
+ public String getColumn1() {
+ return "<div class=\"bigln\">&nbsp;</div>";
+ }
+
+ @Override
+ public String getColumn2() {
+ return "";
+ }
+
+ @Override
+ public String getColumn3() {
+ return "";
+ }
+
+ @Override
+ public String getColumn4() {
+ return "<div class=\"warn\">" + Icons.forPriority(violation.getPriority()).getHTML() + "</img> "
+ + "<a href=\"" + Links.urlForRule(violation.getRuleKey(), false) + "\" onclick=\"window.open(this.href,'rule','height=800,width=900,scrollbars=1,resizable=1');return false;\" title=\"" + violation.getRuleKey() + "\"><b>" + Utils.escapeHtml(violation.getRuleName()) + "</b></a> : " + Utils.escapeHtml(violation.getMessage()) + "</div>";
+ }
+ }
+
+ private boolean hasViolations(int lineIndex) {
+ if (lineIndex < 0) {
+ return false;
+ }
+ List<Violation> list = filteredViolationsByLine.get(lineIndex);
+ return list != null && !list.isEmpty();
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/violationsviewer/client/ViolationsViewer.java b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/violationsviewer/client/ViolationsViewer.java
new file mode 100644
index 00000000000..a79c243cf48
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/violationsviewer/client/ViolationsViewer.java
@@ -0,0 +1,206 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.violationsviewer.client;
+
+import com.google.gwt.event.dom.client.ChangeEvent;
+import com.google.gwt.event.dom.client.ChangeHandler;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.gen2.table.override.client.Grid;
+import com.google.gwt.user.client.ui.*;
+import org.sonar.gwt.Configuration;
+import org.sonar.gwt.Links;
+import org.sonar.gwt.Metrics;
+import org.sonar.gwt.ui.Icons;
+import org.sonar.gwt.ui.Loading;
+import org.sonar.gwt.ui.Page;
+import org.sonar.wsclient.gwt.AbstractCallback;
+import org.sonar.wsclient.gwt.Sonar;
+import org.sonar.wsclient.services.Measure;
+import org.sonar.wsclient.services.Resource;
+import org.sonar.wsclient.services.ResourceQuery;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class ViolationsViewer extends Page {
+ public static final String GWT_ID = "org.sonar.plugins.core.violationsviewer.ViolationsViewer";
+
+ private Resource resource;
+ private final Panel mainPanel = new VerticalPanel();
+ private final Loading loading = new Loading(I18nConstants.INSTANCE.loading());
+
+ // header
+ private Grid header = null;
+ private ListBox filterBox = null;
+ private CheckBox expandCheckbox = null;
+ private String defaultFilter;
+
+ // source
+ private ViolationsPanel sourcePanel;
+
+ private boolean resourceHasViolations = false;
+
+ @Override
+ protected Widget doOnResourceLoad(Resource resource) {
+ this.resource = resource;
+ mainPanel.clear();
+ mainPanel.add(loading);
+ mainPanel.setWidth("100%");
+ mainPanel.setStyleName("gwt-Violations");
+
+ header = new Grid(1, 5);
+ header.setWidth("100%");
+ header.setStylePrimaryName("gwt-ViewerHeader");
+ header.getCellFormatter().setStyleName(0, 0, "thin left");
+
+ initDefaultFilter();
+ sourcePanel = new ViolationsPanel(resource, defaultFilter);
+
+ header.setHTML(0, 1, "<div class='cell'><span class='note'>" + I18nConstants.INSTANCE.filter() + "</span></div>");
+ header.getCellFormatter().setStyleName(0, 1, "right");
+
+ filterBox = new ListBox();
+ filterBox.addItem(I18nConstants.INSTANCE.noFilters(), "");
+ filterBox.setStyleName("small");
+
+ filterBox.addChangeHandler(new ChangeHandler() {
+ public void onChange(ChangeEvent event) {
+ String filter = filterBox.getValue(filterBox.getSelectedIndex());
+ loadSources();
+ sourcePanel.filter(filter);
+ sourcePanel.refresh();
+ }
+ });
+
+ header.setWidget(0, 2, filterBox);
+ header.getCellFormatter().setStyleName(0, 2, "thin cell right");
+
+ header.setHTML(0, 3, "<div class='note'>" + I18nConstants.INSTANCE.expand() + "</div>");
+ header.getCellFormatter().setStyleName(0, 3, "thin right");
+
+ expandCheckbox = new CheckBox();
+ expandCheckbox.addClickHandler(new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ loadSources();
+ sourcePanel.setExpand(expandCheckbox.getValue());
+ sourcePanel.refresh();
+ }
+ });
+ header.setWidget(0, 4, expandCheckbox);
+ header.getCellFormatter().setStyleName(0, 4, "thin cell left");
+
+ loadRulePriorities();
+ return mainPanel;
+ }
+
+ private void initDefaultFilter() {
+ defaultFilter = Configuration.getRequestParameter("rule");
+ if (defaultFilter == null) {
+ defaultFilter = Configuration.getRequestParameter("priority");
+ }
+ }
+
+ private void loadRulePriorities() {
+ final ResourceQuery query = ResourceQuery.createForResource(resource, Metrics.VIOLATIONS)
+ .setExcludeRulePriorities(false);
+ Sonar.getInstance().find(query, new AbstractCallback<Resource>(loading) {
+ @Override
+ protected void doOnResponse(Resource resource) {
+ setResourceHasViolations(resource);
+ displayRulePriorities(resource);
+ loadRules(resource);
+ }
+ });
+ }
+
+ private void displayRulePriorities(Resource resource) {
+ final Grid grid = new Grid(1, 10);
+ header.setWidget(0, 0, grid);
+
+ List<Measure> measures = resource.getMeasures();
+ displayRulePriority(grid, 0, "BLOCKER", measures);
+ displayRulePriority(grid, 2, "CRITICAL", measures);
+ displayRulePriority(grid, 4, "MAJOR", measures);
+ displayRulePriority(grid, 6, "MINOR", measures);
+ displayRulePriority(grid, 8, "INFO", measures);
+ }
+
+ private void displayRulePriority(final Grid grid, final int column, final String priority, final List<Measure> measures) {
+ String value = "0";
+ for (Measure measure : measures) {
+ if (priority.equals(measure.getRulePriority())) {
+ value = measure.getFormattedValue();
+ filterBox.addItem(priority + " (" + value + ")", priority);
+ if (priority.equals(defaultFilter)) {
+ filterBox.setSelectedIndex(filterBox.getItemCount() - 1);
+ }
+ continue;
+ }
+ }
+ grid.setHTML(0, column, Icons.forPriority(priority).getHTML());
+ grid.setHTML(0, column + 1, value);
+ grid.getCellFormatter().setStyleName(0, column, "thin metric right");
+ grid.getCellFormatter().setStyleName(0, column + 1, "thin left value");
+ }
+
+ private void loadRules(Resource resource) {
+ final ResourceQuery query = ResourceQuery.createForResource(resource, Metrics.VIOLATIONS)
+ .setExcludeRules(false);
+ Sonar.getInstance().find(query, new AbstractCallback<Resource>(loading) {
+
+ @Override
+ protected void doOnResponse(Resource resource) {
+ setResourceHasViolations(resource);
+ displayRules(resource);
+ loadSources();
+ }
+ });
+ }
+
+ private void setResourceHasViolations(Resource resource) {
+ resourceHasViolations = resource != null && resource.getMeasure(Metrics.VIOLATIONS) != null;
+ }
+
+ private void displayRules(Resource resource) {
+ Collections.sort(resource.getMeasures(), new Comparator<Measure>() {
+ public int compare(Measure m1, Measure m2) {
+ return m1.getRuleName().compareTo(m2.getRuleName());
+ }
+ });
+ filterBox.addItem("", "");
+ for (Measure measure : resource.getMeasures()) {
+ filterBox.addItem(measure.getRuleName() + " (" + measure.getFormattedValue() + ")", measure.getRuleKey());
+ if (measure.getRuleKey().equals(defaultFilter)) {
+ filterBox.setSelectedIndex(filterBox.getItemCount() - 1);
+ }
+ }
+ loading.removeFromParent();
+ mainPanel.add(header);
+ }
+
+ private void loadSources() {
+ mainPanel.remove(sourcePanel);
+ if (resourceHasViolations || expandCheckbox.getValue()) {
+ mainPanel.add(sourcePanel);
+ }
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/clouds/GwtClouds.gwt.xml b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/clouds/GwtClouds.gwt.xml
new file mode 100644
index 00000000000..cde524c0f66
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/clouds/GwtClouds.gwt.xml
@@ -0,0 +1,11 @@
+<module>
+ <inherits name="com.google.gwt.user.User"/>
+ <inherits name="com.google.gwt.json.JSON"/>
+ <inherits name="com.google.gwt.http.HTTP"/>
+ <inherits name="org.sonar.api.web.gwt.Sonar"/>
+
+ <stylesheet src="clouds.css"/>
+
+ <entry-point class="org.sonar.plugins.core.clouds.client.GwtClouds"/>
+
+</module>
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/clouds/public/clouds.css b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/clouds/public/clouds.css
new file mode 100644
index 00000000000..c2790c3c95c
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/clouds/public/clouds.css
@@ -0,0 +1,29 @@
+.tag {
+ padding: 0px;
+ cursor: pointer;
+}
+
+.inline{
+ display: inline;
+}
+
+a {
+ border-bottom: 0 none;
+}
+
+.tab_title {
+ white-space: nowrap;
+}
+
+.metricSelectBox {
+ float: right;
+}
+
+.metricSelectBox .labelText {
+ padding-top: 2px;
+ padding-right: 5px;
+
+}
+
+
+
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/clouds/public/test.html b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/clouds/public/test.html
new file mode 100644
index 00000000000..c4292e18121
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/clouds/public/test.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <title>Clouds xxx</title>
+ <link href="http://localhost:9000/dev/stylesheets/yui-2.6.0.css" media="all" rel="Stylesheet" type="text/css"/>
+ <link href="http://localhost:9000/dev/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
+ <script src="http://localhost:9000/dev/javascripts/application.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/prototype.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/scriptaculous.js" type="text/javascript"></script>
+</head>
+
+<body>
+<script type="text/javascript">
+ var config = {
+ "sonar_url": "http://localhost:9000/dev",
+ "resource_key" : "org.codehaus.sonar:sonar-plugin-api",
+ "permalink_url_base" : "http://localhost:9000/dev/views/project/org.codehaus.sonar:sonar/org.sonar.plugins.core.clouds.GwtClouds?foo=bar"
+ };
+</script>
+
+<div class="error" id="error" style="display:none"><span id="errormsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('error').hide();return false;">hide</a>]
+</div>
+<div class="warning" id="warning" style="display:none"><span id="warningmsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('warning').hide();return false;">hide</a>]
+</div>
+<div class="notice" id="info" style="display:none"><span id="infomsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('info').hide();return false;">hide</a>]
+</div>
+
+<div id="gwtpage">
+</div>
+
+<script type="text/javascript" language="javascript" src="org.sonar.plugins.core.clouds.GwtClouds.nocache.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/coverageviewer/CoverageViewer.gwt.xml b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/coverageviewer/CoverageViewer.gwt.xml
new file mode 100644
index 00000000000..b14b0e2f735
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/coverageviewer/CoverageViewer.gwt.xml
@@ -0,0 +1,10 @@
+<module>
+ <inherits name="com.google.gwt.user.User"/>
+ <inherits name="com.google.gwt.json.JSON"/>
+ <inherits name="com.google.gwt.http.HTTP"/>
+ <inherits name="org.sonar.Sonar"/>
+ <inherits name="com.google.gwt.gen2.table.Table"/>
+
+ <entry-point class="org.sonar.plugins.core.coverageviewer.client.CoverageViewer"/>
+
+</module>
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/coverageviewer/public/test.html b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/coverageviewer/public/test.html
new file mode 100644
index 00000000000..181a04a6a72
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/coverageviewer/public/test.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <title>Code coverage</title>
+ <link href="http://localhost:9000/dev/stylesheets/yui-2.6.0.css" media="all" rel="Stylesheet" type="text/css"/>
+ <link href="http://localhost:9000/dev/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
+ <script src="http://localhost:9000/dev/javascripts/application.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/prototype.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/scriptaculous.js" type="text/javascript"></script>
+</head>
+
+<body>
+<script type="text/javascript">
+ var registeredTabs = [];
+ var config = {
+ "sonar_url": "http://localhost:9000/dev",
+ "viewer_resource_key": "org.apache.struts:struts-core:org.apache.struts.action.ActionServlet"
+ };
+</script>
+<div class="error" id="error" style="display:none"><span id="errormsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('error').hide();return false;">hide</a>]
+</div>
+<div class="warning" id="warning" style="display:none"><span id="warningmsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('warning').hide();return false;">hide</a>]
+</div>
+<div class="notice" id="info" style="display:none"><span id="infomsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('info').hide();return false;">hide</a>]
+</div>
+
+<div id="resource_viewers">
+ <div id='loading'></div>
+</div>
+<script type="text/javascript" language="javascript"
+ src="org.sonar.plugins.core.coverageviewer.CoverageViewer.nocache.js"></script>
+
+<a href="#" onclick="load_org_sonar_plugins_core_coverageviewer_CoverageViewer();">load</a>
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/defaultsourceviewer/GwtDefaultSourceViewer.gwt.xml b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/defaultsourceviewer/GwtDefaultSourceViewer.gwt.xml
new file mode 100644
index 00000000000..be36aa7ef89
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/defaultsourceviewer/GwtDefaultSourceViewer.gwt.xml
@@ -0,0 +1,10 @@
+<module>
+ <inherits name="com.google.gwt.user.User"/>
+ <inherits name="com.google.gwt.json.JSON"/>
+ <inherits name="com.google.gwt.http.HTTP"/>
+ <inherits name="org.sonar.Sonar"/>
+ <inherits name="com.google.gwt.gen2.table.Table"/>
+
+ <entry-point class="org.sonar.plugins.core.defaultsourceviewer.client.GwtDefaultSourceViewer"/>
+
+</module>
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/defaultsourceviewer/public/test.html b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/defaultsourceviewer/public/test.html
new file mode 100644
index 00000000000..02a9f684766
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/defaultsourceviewer/public/test.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <title>Sources</title>
+ <link href="http://localhost:9000/stylesheets/sonar.css" media="all" rel="Stylesheet" type="text/css" />
+ <script src="http://localhost:9000/javascripts/sonar.js" type="text/javascript"></script>
+</head>
+
+<body>
+<div id="gwtpage"></div>
+<script type="text/javascript">
+var config = {
+ "sonar_url": "http://localhost:9000",
+ "resource":[{"id":97, "key":"org.apache.struts:struts-core:org.apache.struts.config.ConfigRuleSet","scope": "FIL", "qualifier": "CLA", "name": "Struts Core", "lang":"java"}]
+};
+var modules = {};
+var rp = {};
+
+</script>
+<script type="text/javascript" language="javascript" src="org.sonar.plugins.core.defaultsourceviewer.GwtDefaultSourceViewer.nocache.js"></script>
+
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/duplicationsviewer/DuplicationsViewer.gwt.xml b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/duplicationsviewer/DuplicationsViewer.gwt.xml
new file mode 100644
index 00000000000..b480cc0b418
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/duplicationsviewer/DuplicationsViewer.gwt.xml
@@ -0,0 +1,13 @@
+<module>
+ <inherits name="com.google.gwt.xml.XML"/>
+ <inherits name="com.google.gwt.user.User"/>
+ <inherits name="com.google.gwt.json.JSON"/>
+ <inherits name="com.google.gwt.http.HTTP"/>
+ <inherits name="org.sonar.Sonar"/>
+ <inherits name="com.google.gwt.gen2.table.Table"/>
+
+ <stylesheet src="DuplicationsViewer.css"/>
+
+ <entry-point class="org.sonar.plugins.core.duplicationsviewer.client.DuplicationsViewer"/>
+
+</module>
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/duplicationsviewer/public/DuplicationsViewer.css b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/duplicationsviewer/public/DuplicationsViewer.css
new file mode 100644
index 00000000000..2cc0ced131c
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/duplicationsviewer/public/DuplicationsViewer.css
@@ -0,0 +1,48 @@
+.gwt-DuplicationsPanel {
+ width: 100%;
+}
+
+.duplicationsTable {
+ border: 1px solid #C0C0C0;
+ width: 100%;
+ vertical-align: top;
+}
+
+.duplicationsTable td {
+ vertical-align: top;
+ text-align: left;
+ padding: 3px;
+ white-space: nowrap;
+}
+
+.duplicationsTable #expandCollapseCol {
+ width: 60px;
+}
+
+.duplicationsTable #nbLineCol {
+ width: 80px;
+}
+
+.duplicationsTable #lineFromCol {
+ width: 80px;
+}
+
+.duplicationsTable #fileCol {
+ width: 80px;
+}
+
+.duplicationsTable .header {
+ background-color: #EFEFEF;
+ font-weight: bold;
+ color: #333;
+ vertical-align: top;
+}
+
+.expandCollapseLink {
+ margin-left: 5px;
+}
+
+.gwt-SourcePanel table.sources td {
+ vertical-align: top;
+ padding: 0px;
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/duplicationsviewer/public/test.html b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/duplicationsviewer/public/test.html
new file mode 100644
index 00000000000..37b1bb43253
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/duplicationsviewer/public/test.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <title>Duplications</title>
+ <link href="http://localhost:9000/dev/stylesheets/yui-2.6.0.css" media="all" rel="Stylesheet" type="text/css"/>
+ <link href="http://localhost:9000/dev/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
+ <script src="http://localhost:9000/dev/javascripts/application.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/prototype.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/scriptaculous.js" type="text/javascript"></script>
+</head>
+
+<body>
+<script type="text/javascript">
+ var registeredTabs = [];
+ var config = {
+ "sonar_url": "http://localhost:9000/dev",
+ "viewer_resource_key" : "org.sonar.tests:reference:org.sonar.samples.duplicated_lines_within_same_class.DuplicatedLinesInSameClass"
+ };
+
+
+</script>
+<div class="error" id="error" style="display:none"><span id="errormsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('error').hide();return false;">hide</a>]
+</div>
+<div class="warning" id="warning" style="display:none"><span id="warningmsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('warning').hide();return false;">hide</a>]
+</div>
+<div class="notice" id="info" style="display:none"><span id="infomsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('info').hide();return false;">hide</a>]
+</div>
+
+<div id="resource_viewers">
+ <div id='loading'></div>
+</div>
+<script type="text/javascript" language="javascript"
+ src="org.sonar.plugins.core.duplicationsviewer.DuplicationsViewer.nocache.js"></script>
+
+<a href="#" onclick="load_org_sonar_plugins_core_duplicationsviewer_DuplicationsViewer();">load</a>
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/hotspots/GwtHotspots.gwt.xml b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/hotspots/GwtHotspots.gwt.xml
new file mode 100644
index 00000000000..574aa03fedc
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/hotspots/GwtHotspots.gwt.xml
@@ -0,0 +1,15 @@
+<module>
+ <inherits name="com.google.gwt.user.User"/>
+ <inherits name="com.google.gwt.json.JSON"/>
+ <inherits name="com.google.gwt.http.HTTP"/>
+ <inherits name="org.sonar.Sonar"/>
+ <inherits name="com.google.gwt.i18n.I18N"/>
+
+ <stylesheet src="hotspots.css"/>
+
+ <entry-point class="org.sonar.plugins.core.hotspots.client.GwtHotspots"/>
+
+ <extend-property name="locale" values="en"/>
+ <extend-property name="locale" values="fr"/>
+
+</module> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/hotspots/client/I18nConstants_fr.properties b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/hotspots/client/I18nConstants_fr.properties
new file mode 100644
index 00000000000..efd8a8c42d6
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/hotspots/client/I18nConstants_fr.properties
@@ -0,0 +1,16 @@
+# This file must use UTF-8 encoding
+
+titleMostViolatedRules=Règles les moins respectées
+titleMostViolatedResources=Les moins respectueux des règles
+titleLongestTests=Les plus long tests
+titleMostComplexResources=Les plus complexes
+titleMostDuplicatedResources=Les plus dupliqués
+titleLessTested=Le plus de lignes non testées
+titleMostComplexMethods=Avec les méthodes les plus complexes
+titleMostUndocumentedAPI=Les API les moins documentées
+noMeasures=Aucune mesure
+anyPriority=Toutes les priorités
+moreDetails=Détails
+lcom4=Manque de cohésion entre méthodes
+rfc=Response for class
+designTitle= \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/hotspots/public/hotspots.css b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/hotspots/public/hotspots.css
new file mode 100644
index 00000000000..6ae0b1d2747
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/hotspots/public/hotspots.css
@@ -0,0 +1,102 @@
+.gwt-Hotspots {
+ width: 780px;
+}
+
+.gwt-Hotspots td {
+ width: 380px;
+ vertical-align: top;
+}
+
+.gwt-HotspotPanel {
+ width: 100%;
+}
+
+.gwt-HotspotPanel .header {
+ color: #333;
+ font-size: 93%;
+ font-weight: bold;
+ padding: 3px 0px 3px 5px;
+ white-space: nowrap;
+}
+.hotspotcol {
+ vertical-align: top;
+}
+
+.gwt-Hotspot {
+ border: 1px solid #ccc;
+ border-top-width: 2px;
+ border-bottom-width: 2px;
+ margin-left: 5px;
+ margin-right: 5px;
+ margin-bottom: 10px;
+ width: 390px;
+}
+
+.gwt-Hotspot td {
+ padding: 3px 4px;
+ line-height: 18px;
+}
+
+.gwt-Hotspot td.small {
+ padding: 3px 1px;
+}
+
+.gwt-Hotspot .header {
+ color: #333;
+ font-size: 93%;
+ font-weight: bold;
+ padding: 4px 0 3px 9px;
+}
+
+.gwt-Hotspot .resourceCell {
+ width: 99%;
+}
+
+.gwt-Hotspot .ccResourceCell {
+ width: 60%;
+}
+
+.gwt-Hotspot .resultCell {
+ width: 1%;
+ white-space: nowrap;
+ text-align: right;
+ border-right: none;
+}
+
+.gwt-Hotspot .ccResultCell {
+ border-right: 1px solid #C0C0C0;
+ width: 1%;
+ white-space: nowrap;
+ text-align: right;
+}
+
+.gwt-Hotspot .graphCell {
+ width: 1%;
+ white-space: nowrap;
+ text-align: right;
+ border-left: none;
+}
+
+.gwt-Hotspot .header.ccHeaderResource {
+ width: 60%;
+}
+
+.gwt-Hotspot .header.headerResource {
+ width: 70%;
+}
+
+.gwt-Hotspot .header.headerEmptyResults {
+ width: 100%;
+}
+
+.gwt-Hotspot .header.headerResult {
+ width: 20%;
+}
+
+.gwt-Hotspot .header.ccHeaderResult {
+ width: 30%;
+}
+
+.gwt-Hotspot .header.headerGraph {
+ width: 10%;
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/hotspots/public/test.html b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/hotspots/public/test.html
new file mode 100644
index 00000000000..d66011fb116
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/hotspots/public/test.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <title>Hotspots</title>
+ <link href="http://localhost:9000/stylesheets/sonar.css" media="all" rel="Stylesheet" type="text/css"/>
+ <script src="http://localhost:9000/javascripts/sonar.js" type="text/javascript"></script>
+</head>
+
+<body>
+<script type="text/javascript">
+ var config = {
+ "sonar_url": "http://localhost:9000",
+ "resource_key" : "org.apache.struts:struts-parent"
+ };
+</script>
+
+<div class="error" id="error" style="display:none"><span id="errormsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('error').hide();return false;">hide</a>]
+</div>
+<div class="warning" id="warning" style="display:none"><span id="warningmsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('warning').hide();return false;">hide</a>]
+</div>
+<div class="notice" id="info" style="display:none"><span id="infomsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('info').hide();return false;">hide</a>]
+</div>
+
+<div id="gwtpage">
+</div>
+
+<script type="text/javascript" language="javascript"
+ src="org.sonar.plugins.core.hotspots.GwtHotspots.nocache.js"></script>
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/TestsViewer.gwt.xml b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/TestsViewer.gwt.xml
new file mode 100644
index 00000000000..c5e1c33bf6d
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/TestsViewer.gwt.xml
@@ -0,0 +1,13 @@
+<module>
+ <inherits name="com.google.gwt.xml.XML"/>
+ <inherits name="com.google.gwt.user.User"/>
+ <inherits name="com.google.gwt.json.JSON"/>
+ <inherits name="com.google.gwt.http.HTTP"/>
+ <inherits name="org.sonar.Sonar"/>
+ <inherits name="com.google.gwt.gen2.table.Table"/>
+
+ <stylesheet src="TestsViewer.css"/>
+
+ <entry-point class="org.sonar.plugins.core.testdetailsviewer.client.TestsViewer"/>
+
+</module> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/TestsViewer.css b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/TestsViewer.css
new file mode 100644
index 00000000000..0ce428402b6
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/TestsViewer.css
@@ -0,0 +1,88 @@
+.gwt-TestDetailsPanel {
+ width: 100%;
+}
+
+.gwt-TestDetailsPanel .error {
+ background-image: url("images/error.png");
+ background-position: 4px 2px;
+ background-repeat: no-repeat;
+ background-color: transparent;
+ border: none;
+ margin: 0 0 2px;
+ padding: 2px;
+ vertical-align: top;
+}
+
+.gwt-TestDetailsPanel .failure {
+ background-image: url("images/failure.png");
+ background-position: 4px 2px;
+ background-repeat: no-repeat;
+ margin: 0 0 2px;
+ padding: 2px;
+ vertical-align: top;
+}
+
+.gwt-TestDetailsPanel .skipped {
+ background-image: url("images/skipped.png");
+ background-position: 4px 2px;
+ background-repeat: no-repeat;
+ margin: 0 0 2px;
+ padding: 2px;
+ vertical-align: top;
+}
+
+.gwt-TestDetailsPanel .ok {
+ background-image: url("images/ok.png");
+ background-position: 4px 2px;
+ background-repeat: no-repeat;
+ margin: 0 0 2px;
+ padding: 2px;
+ vertical-align: top;
+}
+
+.detailsTable {
+ border: 1px solid #C0C0C0;
+ width: 100%;
+ vertical-align: top;
+}
+
+.detailsTable td {
+ vertical-align: top;
+ text-align: left;
+ padding: 3px;
+}
+
+.detailsTable .header {
+ background-color: #EFEFEF;
+ font-weight: bold;
+ color: #333;
+ vertical-align: top;
+}
+
+.detailsTable #iCol {
+ width: 25px;
+}
+
+.detailsTable #dCol {
+ width: 70px;
+}
+
+.detailsTable #expandCollapseCol {
+ width: 60px;
+ text-align: right;
+}
+
+.detailsTable #noLinkExpandCollapseCol {
+ width: 0px;
+ padding: 0px;
+}
+
+.stackPanel {
+ padding: 5px;
+ border: 1px solid #C0C0C0;
+}
+
+.expandCollapseLink {
+ text-align: right;
+ margin-right: 5px;
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/error.png b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/error.png
new file mode 100644
index 00000000000..c37bd062e60
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/error.png
Binary files differ
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/failure.png b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/failure.png
new file mode 100644
index 00000000000..628cf2dae3d
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/failure.png
Binary files differ
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/ok.png b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/ok.png
new file mode 100644
index 00000000000..89c8129a490
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/ok.png
Binary files differ
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/skipped.png b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/skipped.png
new file mode 100644
index 00000000000..5c870176d4d
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/images/skipped.png
Binary files differ
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/test.html b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/test.html
new file mode 100644
index 00000000000..6ec71b51686
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/testdetailsviewer/public/test.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <title>TestDetails</title>
+ <link href="http://localhost:9000/dev/stylesheets/yui-2.6.0.css" media="all" rel="Stylesheet" type="text/css"/>
+ <link href="http://localhost:9000/dev/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
+ <script src="http://localhost:9000/dev/javascripts/application.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/prototype.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/scriptaculous.js" type="text/javascript"></script>
+</head>
+
+<body>
+<script type="text/javascript">
+ var registeredTabs = [];
+ var config = {
+ "sonar_url": "http://localhost:9000/dev",
+ "viewer_resource_key" : "org.sonar.tests.test-failures:moduleA:ch.hortis.sonar.samples.testFailures.moduleA.FailTest"
+ };
+</script>
+<div class="error" id="error" style="display:none"><span id="errormsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('error').hide();return false;">hide</a>]
+</div>
+<div class="warning" id="warning" style="display:none"><span id="warningmsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('warning').hide();return false;">hide</a>]
+</div>
+<div class="notice" id="info" style="display:none"><span id="infomsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('info').hide();return false;">hide</a>]
+</div>
+
+<div id="resource_viewers">
+ <div id='loading'></div>
+</div>
+<script type="text/javascript" language="javascript"
+ src="org.sonar.plugins.core.testdetailsviewer.TestsViewer.nocache.js"></script>
+
+<a href="#" onclick="load_org_sonar_plugins_core_testdetailsviewer_TestsViewer();">load</a>
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/ui/pageselector/PageSelector.gwt.xml b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/ui/pageselector/PageSelector.gwt.xml
new file mode 100644
index 00000000000..d7ec90cf690
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/ui/pageselector/PageSelector.gwt.xml
@@ -0,0 +1,14 @@
+<module>
+ <inherits name="com.google.gwt.user.User"/>
+ <inherits name="com.google.gwt.json.JSON"/>
+ <inherits name="com.google.gwt.http.HTTP"/>
+ <inherits name="org.sonar.api.web.gwt.Sonar"/>
+ <inherits name="org.sonar.Sonar"/>
+ <stylesheet src="pageselector.css" />
+
+ <entry-point class="org.sonar.plugins.core.ui.pageselector.client.PageSelector"/>
+
+ <extend-property name="locale" values="en"/>
+ <extend-property name="locale" values="fr"/>
+
+</module> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/ui/pageselector/client/I18nConstants_fr.properties b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/ui/pageselector/client/I18nConstants_fr.properties
new file mode 100644
index 00000000000..067f7231c81
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/ui/pageselector/client/I18nConstants_fr.properties
@@ -0,0 +1,2 @@
+# This file must use UTF-8 encoding
+newWindow=Nouvelle fenêtre \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/ui/pageselector/public/pageselector.css b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/ui/pageselector/public/pageselector.css
new file mode 100644
index 00000000000..367e5632efb
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/ui/pageselector/public/pageselector.css
@@ -0,0 +1,25 @@
+/* #rvs is "resource viewer selector" */
+#rvs {
+ width: 100%;
+ margin-top: 10px;
+ border: 1px solid #CCC;
+}
+#rvstitle {
+ width: 100%;
+ margin: 5px;
+}
+#rvstitle .name {
+}
+#rvstitle .right {
+ float: none;
+ text-align: right;
+}
+#rvs .command {
+ text-align: right;
+ cursor: pointer;
+ margin-right: 15px;
+}
+#rvs .command a {
+ color: #444;
+ text-decoration: underline;
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/ui/pageselector/public/test.html b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/ui/pageselector/public/test.html
new file mode 100644
index 00000000000..0f1e49dd8f9
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/ui/pageselector/public/test.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <title>Page selector</title>
+ <link href="http://localhost:9000/stylesheets/sonar.css" media="all" rel="Stylesheet" type="text/css" />
+ <script src="http://localhost:9000/javascripts/sonar.js" type="text/javascript"></script>
+</head>
+
+<body>
+<script type="text/javascript">
+ var config = {
+ "sonar_url": "http://localhost:9000",
+ "version": "2.0",
+ "metric": "ncloc",
+ "resource" : null
+ };
+ var pages = [
+ {"id": "org.sonar.plugins.foo.Foo", "gwt": false, "url": "http://localhost:9000/project/index/1", "metrics":[], "scopes":["PRJ"], "qualifiers": ["TRK", "BRC"], "langs": ["java"]},
+ {"id": "org.sonar.plugins.core.clouds.GwtClouds", "gwt": true, "metrics":["lcom4","noc"], "scopes":["PRJ"], "qualifiers": ["TRK", "BRC"], "langs": ["java"]},
+ {"id": "org.sonar.plugins.Overview", "gwt": true, "metrics":[], "scopes":[], "qualifiers": [], "langs": []},
+ {"id": "org.sonar.plugins.Rails", "gwt": false, "metrics":["clirr"], "scopes":[], "qualifiers": ["FIL"], "langs": []}
+
+ ];
+ var entrypoints = {}
+
+</script>
+<div class="error" id="error" style="display:none"><span id="errormsg"></span> &nbsp;&nbsp;[<a href="#" onclick="javascript:$('error').hide();return false;">hide</a>]</div>
+<div class="warning" id="warning" style="display:none"><span id="warningmsg"></span> &nbsp;&nbsp;[<a href="#" onclick="javascript:$('warning').hide();return false;">hide</a>]</div>
+<div class="notice" id="info" style="display:none"><span id="infomsg"></span> &nbsp;&nbsp;[<a href="#" onclick="javascript:$('info').hide();return false;">hide</a>]</div>
+
+<a href="#" onclick="sr('org.apache.struts:struts-parent');">load struts</a>
+
+<div id="pageselector"> </div>
+
+<script type="text/javascript" language="javascript" src="org.sonar.plugins.core.ui.pageselector.PageSelector.nocache.js"></script>
+
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/violationsviewer/ViolationsViewer.gwt.xml b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/violationsviewer/ViolationsViewer.gwt.xml
new file mode 100644
index 00000000000..473ccebe360
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/violationsviewer/ViolationsViewer.gwt.xml
@@ -0,0 +1,13 @@
+<module>
+ <inherits name="com.google.gwt.user.User"/>
+ <inherits name="com.google.gwt.json.JSON"/>
+ <inherits name="com.google.gwt.http.HTTP"/>
+ <inherits name="org.sonar.Sonar"/>
+ <inherits name="com.google.gwt.gen2.table.Table"/>
+ <inherits name="com.google.gwt.i18n.I18N"/>
+
+ <entry-point class="org.sonar.plugins.core.violationsviewer.client.ViolationsViewer"/>
+
+ <extend-property name="locale" values="en"/>
+ <extend-property name="locale" values="fr"/>
+</module>
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/violationsviewer/client/I18nConstants_fr.properties b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/violationsviewer/client/I18nConstants_fr.properties
new file mode 100644
index 00000000000..73a4d8909a0
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/violationsviewer/client/I18nConstants_fr.properties
@@ -0,0 +1,5 @@
+# This file must use UTF-8 encoding
+filter=Filtre:
+noFilters=Aucun filtre
+expand=Développer:
+loading=En cours de chargement... \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/violationsviewer/public/test.html b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/violationsviewer/public/test.html
new file mode 100644
index 00000000000..7c0a06d111e
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/violationsviewer/public/test.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <title>Violations</title>
+ <link href="http://localhost:9000/dev/stylesheets/yui-2.6.0.css" media="all" rel="Stylesheet" type="text/css"/>
+ <link href="http://localhost:9000/dev/stylesheets/style.css" media="all" rel="Stylesheet" type="text/css"/>
+ <script src="http://localhost:9000/dev/javascripts/application.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/prototype.js" type="text/javascript"></script>
+ <script src="http://localhost:9000/dev/javascripts/scriptaculous.js" type="text/javascript"></script>
+</head>
+
+<body>
+<script type="text/javascript">
+ var registeredTabs = [];
+ var config = {
+ "sonar_url": "http://localhost:9000/dev",
+ "viewer_resource_key": "org.sonar.tests:reference:org.sonar.samples.PrivateClass"
+ };
+ var request_parameters = {"priority": "BLOCKER"}
+</script>
+<div class="error" id="error" style="display:none"><span id="errormsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('error').hide();return false;">hide</a>]
+</div>
+<div class="warning" id="warning" style="display:none"><span id="warningmsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('warning').hide();return false;">hide</a>]
+</div>
+<div class="notice" id="info" style="display:none"><span id="infomsg"></span> &nbsp;&nbsp;[<a href="#"
+ onclick="javascript:$('info').hide();return false;">hide</a>]
+</div>
+
+<div id="resource_viewers">
+</div>
+<script type="text/javascript" language="javascript"
+ src="org.sonar.plugins.core.violationsviewer.ViolationsViewer.nocache.js"></script>
+
+<a href="#" onclick="load_org_sonar_plugins_core_violationsviewer_ViolationsViewer();">load</a>
+</body>
+</html> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/CorePluginTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/CorePluginTest.java
new file mode 100644
index 00000000000..b9dd13aaa14
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/CorePluginTest.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.plugins.core;
+
+import static org.hamcrest.number.OrderingComparisons.greaterThan;
+import static org.junit.Assert.assertThat;
+import org.junit.Test;
+
+public class CorePluginTest {
+
+ @Test
+ public void shouldDefineManyExtensions() {
+ assertThat(new CorePlugin().getExtensions().size(), greaterThan(10));
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/batch/ExcludedResourceFilterTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/batch/ExcludedResourceFilterTest.java
new file mode 100644
index 00000000000..a6066489274
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/batch/ExcludedResourceFilterTest.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.plugins.core.batch;
+
+import org.junit.Test;
+import org.sonar.api.resources.Resource;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ExcludedResourceFilterTest {
+
+ @Test
+ public void doNotFailIfNoPatterns() {
+ ExcludedResourceFilter filter = new ExcludedResourceFilter((String[]) null);
+ assertThat(filter.isIgnored(mock(Resource.class)), is(false));
+ }
+
+ @Test
+ public void noPatternsMatch() {
+ ExcludedResourceFilter filter = new ExcludedResourceFilter(new String[]{"**/foo/*.java", "**/bar/*"});
+ assertThat(filter.isIgnored(mock(Resource.class)), is(false));
+ }
+
+ /**
+ * See SONAR-1115 Exclusion patterns do not apply to unit tests.
+ */
+ @Test
+ public void ignoreResourceIfMatchesPattern() {
+ ExcludedResourceFilter filter = new ExcludedResourceFilter(new String[]{"**/foo/*.java", "**/bar/*"});
+
+ Resource resource = mock(Resource.class);
+ when(resource.matchFilePattern("**/bar/*")).thenReturn(true);
+
+ assertThat(filter.isIgnored(resource), is(true));
+ }
+
+ @Test
+ public void doNotExcludeUnitTestFiles() {
+ ExcludedResourceFilter filter = new ExcludedResourceFilter(new String[]{"**/foo/*.java", "**/bar/*"});
+
+ Resource unitTest = mock(Resource.class);
+ when(unitTest.getQualifier()).thenReturn(Resource.QUALIFIER_UNIT_TEST_CLASS);
+
+ // match exclusion pattern
+ when(unitTest.matchFilePattern("**/bar/*")).thenReturn(true);
+
+ assertThat(filter.isIgnored(unitTest), is(false));
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/charts/DistributionAreaChartTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/charts/DistributionAreaChartTest.java
new file mode 100644
index 00000000000..3be2879ad12
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/charts/DistributionAreaChartTest.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.plugins.core.charts;
+
+import org.junit.Test;
+import org.sonar.api.charts.AbstractChartTest;
+import org.sonar.api.charts.ChartParameters;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+
+public class DistributionAreaChartTest extends AbstractChartTest {
+
+ @Test
+ public void oneSerie() throws IOException {
+ DistributionAreaChart chart = new DistributionAreaChart();
+ BufferedImage image = chart.generateImage(new ChartParameters("v=0%3D5%3B1%3D22%3B2%3D2"));
+ assertChartSizeGreaterThan(image, 1000);
+ saveChart(image, "DistributionAreaChartTest/oneSerie.png");
+ }
+
+ @Test
+ public void manySeries() throws IOException {
+ DistributionAreaChart chart = new DistributionAreaChart();
+ BufferedImage image = chart.generateImage(new ChartParameters("v=0%3D5%3B1%3D22%3B2%3D2|0%3D7%3B1%3D15%3B2%3D4"));
+ assertChartSizeGreaterThan(image, 1000);
+ saveChart(image, "DistributionAreaChartTest/manySeries.png");
+ }
+
+ @Test
+ public void manySeriesWithDifferentCategories() throws IOException {
+ DistributionAreaChart chart = new DistributionAreaChart();
+ BufferedImage image = chart.generateImage(new ChartParameters("v=0%3D5%3B1%3D22%3B2%3D2|2%3D7%3B4%3D15%3B9%3D4"));
+ assertChartSizeGreaterThan(image, 1000);
+ saveChart(image, "DistributionAreaChartTest/manySeriesWithDifferentCategories.png");
+ }
+
+ @Test
+ public void manySeriesIncludingAnEmptySerie() throws IOException {
+ // the third serie should not have the second default color, but the third one !
+ DistributionAreaChart chart = new DistributionAreaChart();
+ BufferedImage image = chart.generateImage(new ChartParameters("v=0%3D5%3B1%3D22%3B2%3D2||2%3D7%3B4%3D15%3B9%3D4"));
+ assertChartSizeGreaterThan(image, 1000);
+ saveChart(image, "DistributionAreaChartTest/manySeriesIncludingAnEmptySerie.png");
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/charts/DistributionBarChartTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/charts/DistributionBarChartTest.java
new file mode 100644
index 00000000000..b83ed6f81ed
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/charts/DistributionBarChartTest.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.plugins.core.charts;
+
+import org.junit.Test;
+import org.sonar.api.charts.AbstractChartTest;
+import org.sonar.api.charts.ChartParameters;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+
+public class DistributionBarChartTest extends AbstractChartTest {
+
+ @Test
+ public void simpleSample() throws IOException {
+ DistributionBarChart chart = new DistributionBarChart();
+ BufferedImage image = chart.generateImage(new ChartParameters("v=0%3D5%3B1%3D22%3B2%3D2"));
+ assertChartSizeGreaterThan(image, 1000);
+ saveChart(image, "DistributionBarChartTest/simpleSample.png");
+ }
+
+ @Test
+ public void addXSuffix() throws IOException {
+ DistributionBarChart chart = new DistributionBarChart();
+ // should suffix x labels with +
+ BufferedImage image = chart.generateImage(new ChartParameters("v=0%3D5%3B1%3D22%3B2%3D2&xsuf=%2B"));
+ assertChartSizeGreaterThan(image, 1000);
+ saveChart(image, "DistributionBarChartTest/addXSuffix.png");
+ }
+
+ @Test
+ public void addYSuffix() throws IOException {
+ DistributionBarChart chart = new DistributionBarChart();
+ // should suffix y labels with %
+ BufferedImage image = chart.generateImage(new ChartParameters("v=0%3D5%3B1%3D22%3B2%3D2&ysuf=%25"));
+ assertChartSizeGreaterThan(image, 1000);
+ saveChart(image, "DistributionBarChartTest/addYSuffix.png");
+ }
+
+ @Test
+ public void manySeries() throws IOException {
+ DistributionBarChart chart = new DistributionBarChart();
+ BufferedImage image = chart.generateImage(new ChartParameters("v=0%3D5%3B1%3D22%3B2%3D2|0%3D7%3B1%3D15%3B2%3D4"));
+ assertChartSizeGreaterThan(image, 1000);
+ saveChart(image, "DistributionBarChartTest/manySeries.png");
+ }
+
+ @Test
+ public void manySeriesIncludingAnEmptySerie() throws IOException {
+ DistributionBarChart chart = new DistributionBarChart();
+ // the third serie should not have the second default color, but the third one !
+ BufferedImage image = chart.generateImage(new ChartParameters("v=0%3D5%3B1%3D22%3B2%3D2||0%3D7%3B1%3D15%3B2%3D4"));
+ assertChartSizeGreaterThan(image, 1000);
+ saveChart(image, "DistributionBarChartTest/manySeriesIncludingAnEmptySerie.png");
+ }
+
+ @Test
+ public void overridenSize() throws IOException {
+ DistributionBarChart chart = new DistributionBarChart();
+ BufferedImage image = chart.generateImage(new ChartParameters("v=0%3D5%3B1%3D22%3B2%3D2|0%3D7%3B1%3D15%3B2%3D4&w=500&h=200"));
+ assertChartSizeGreaterThan(image, 1000);
+ saveChart(image, "DistributionBarChartTest/overridenSize.png");
+ }
+
+ @Test
+ public void changeColor() throws IOException {
+ DistributionBarChart chart = new DistributionBarChart();
+ BufferedImage image = chart.generateImage(new ChartParameters("v=0%3D5%3B1%3D22%3B2%3D2&c=777777&bgc=777777"));
+ assertChartSizeGreaterThan(image, 1000);
+ saveChart(image, "DistributionBarChartTest/changeColor.png");
+ }
+
+ @Test
+ public void smallSize() throws IOException {
+ DistributionBarChart chart = new DistributionBarChart();
+ BufferedImage image = chart.generateImage(new ChartParameters("v=0%3D5%3B1%3D22%3B2%3D2%3B4%3D22%3B5%3D22%3B6%3D22&c=777777&w=120&h=80&fs=8"));
+ assertChartSizeGreaterThan(image, 500);
+ saveChart(image, "DistributionBarChartTest/smallSize.png");
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/charts/XradarChartTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/charts/XradarChartTest.java
new file mode 100644
index 00000000000..8f9cb7eb745
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/charts/XradarChartTest.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.plugins.core.charts;
+
+import org.junit.Test;
+import org.sonar.api.charts.AbstractChartTest;
+import org.sonar.api.charts.ChartParameters;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+
+public class XradarChartTest extends AbstractChartTest {
+
+ @Test
+ public void shouldGenerateXradar() throws IOException {
+ String url = "w=200&h=200&l=Usa.,Eff.,Rel.,Main.,Por.&g=0.25&m=100&v=90,80,70,60,50|40,30,20,10,10&c=CAE3F2|F8A036";
+ XradarChart radar = new XradarChart();
+ BufferedImage img = radar.generateImage(new ChartParameters(url));
+ saveChart(img, "shouldGenerateXradar.png");
+ assertChartSizeGreaterThan(img, 50);
+ }
+
+ @Test
+ public void negativeValuesAreNotDisplayed() throws IOException {
+ String url = "w=200&h=200&l=Usa.,Eff.,Rel.,Main.,Por.&g=0.3&m=100&v=-90,-80,70,60,50&c=CAE3F2";
+ XradarChart radar = new XradarChart();
+ BufferedImage img = radar.generateImage(new ChartParameters(url));
+ saveChart(img, "negativeValuesAreNotDisplayed.png");
+
+ // you have to check visually that it does not work ! Min value is 0. This is a limitation of JFreeChart.
+ assertChartSizeGreaterThan(img, 50);
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/metrics/UserManagedMetricsTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/metrics/UserManagedMetricsTest.java
new file mode 100644
index 00000000000..72158fdbe88
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/metrics/UserManagedMetricsTest.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.plugins.core.metrics;
+
+import static org.hamcrest.Matchers.greaterThan;
+import static org.junit.Assert.assertThat;
+import org.junit.Test;
+
+public class UserManagedMetricsTest {
+
+ @Test
+ public void checkDefinitions() {
+ UserManagedMetrics metrics = new UserManagedMetrics();
+ assertThat(metrics.getMetrics().size(), greaterThan(2));
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeDeletedResourcesTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeDeletedResourcesTest.java
new file mode 100644
index 00000000000..b662d1e1cac
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeDeletedResourcesTest.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.plugins.core.purges;
+
+import org.junit.Test;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+public class PurgeDeletedResourcesTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void purgeDeletedResources() throws SQLException {
+ setupData("sharedFixture", "purgeDeletedResources");
+
+ final Connection c = getConnection().getConnection();
+ c.prepareStatement("SET REFERENTIAL_INTEGRITY FALSE; ").execute();
+ c.prepareStatement("delete from projects where id=3").executeUpdate();
+ c.commit();
+
+ final PurgeDeletedResources purge = new PurgeDeletedResources(getSession());
+ purge.purge(null);
+
+ checkTables("purgeDeletedResources", "snapshots", "project_measures", "measure_data", "rule_failures", "snapshot_sources");
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeDeprecatedLastTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeDeprecatedLastTest.java
new file mode 100644
index 00000000000..c4b9f82ead9
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeDeprecatedLastTest.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.plugins.core.purges;
+
+import org.junit.Test;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.sql.SQLException;
+
+public class PurgeDeprecatedLastTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void purgeDeprecatedLast() throws SQLException {
+ setupData("sharedFixture", "purgeDeprecatedLast");
+
+ new PurgeDeprecatedLast(getSession()).purge(null);
+
+ checkTables("purgeDeprecatedLast", "snapshots", "project_measures", "measure_data", "rule_failures", "snapshot_sources");
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest.java
new file mode 100644
index 00000000000..08138016e5f
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest.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.plugins.core.purges;
+
+import org.junit.Test;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.sql.SQLException;
+
+public class PurgeDisabledResourcesTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void purgeDisabledModule() throws SQLException {
+ assertPurge("purgeDisabledModule");
+ }
+
+ @Test
+ public void purgeDisabledProject() throws SQLException {
+ assertPurge("purgeDisabledProject");
+ }
+
+ @Test
+ public void nothingToPurge() throws SQLException {
+ assertPurge("nothingToPurge");
+ }
+
+ private void assertPurge(String testName) {
+ setupData("sharedFixture", testName);
+ new PurgeDisabledResources(getSession()).purge(null);
+ checkTables(testName, "snapshots", "project_measures");
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeEntitiesTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeEntitiesTest.java
new file mode 100644
index 00000000000..aa354af823f
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeEntitiesTest.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.plugins.core.purges;
+
+import org.junit.Test;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+public class PurgeEntitiesTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void purgeEntities() {
+ setupData("sharedFixture", "purgeEntities");
+
+ final PurgeEntities purge = new PurgeEntities(getSession());
+ purge.purge(null);
+
+ checkTables("purgeEntities", "snapshots", "project_measures", "measure_data", "rule_failures", "snapshot_sources");
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeEventOrphansTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeEventOrphansTest.java
new file mode 100644
index 00000000000..6712eb6f69e
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeEventOrphansTest.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.plugins.core.purges;
+
+import org.junit.Test;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.sql.SQLException;
+
+public class PurgeEventOrphansTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void purgeEventOrphans() throws SQLException {
+ assertPurge("purgeEventOrphans");
+ }
+
+ private void assertPurge(String testName) {
+ setupData(testName);
+ new PurgeEventOrphans(getSession()).purge(null);
+ checkTables(testName, "events");
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeOrphanResourcesTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeOrphanResourcesTest.java
new file mode 100644
index 00000000000..fdbb986d9d3
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeOrphanResourcesTest.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.plugins.core.purges;
+
+import org.junit.Test;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.sql.SQLException;
+
+public class PurgeOrphanResourcesTest extends AbstractDbUnitTestCase {
+ @Test
+ public void purgeOrphanResources() throws SQLException {
+ assertPurge("purgeOrphanResources");
+ }
+
+ private void assertPurge(String testName) {
+ setupData(testName);
+ new PurgeOrphanResources(getSession()).purge(null);
+ checkTables(testName, "projects");
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgePropertyOrphansTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgePropertyOrphansTest.java
new file mode 100644
index 00000000000..277fe317c61
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgePropertyOrphansTest.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.plugins.core.purges;
+
+import org.junit.Test;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.sql.SQLException;
+
+public class PurgePropertyOrphansTest extends AbstractDbUnitTestCase {
+ @Test
+ public void purgeResourceOrphans() throws SQLException {
+ setupData("purgeResourceOrphans");
+ new PurgePropertyOrphans(getSession()).purgeResourceOrphans();
+ checkTables("purgeResourceOrphans", "properties");
+ }
+
+ @Test
+ public void purgeUserOrphans() throws SQLException {
+ setupData("purgeUserOrphans");
+ new PurgePropertyOrphans(getSession()).purgeUserOrphans();
+ checkTables("purgeUserOrphans", "properties");
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeResourceRolesTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeResourceRolesTest.java
new file mode 100644
index 00000000000..041a888eca3
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeResourceRolesTest.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.plugins.core.purges;
+
+import org.junit.Test;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.sql.SQLException;
+
+public class PurgeResourceRolesTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void purgeResourceRoles() throws SQLException {
+ setupData("purgeResourceRoles");
+
+ new PurgeResourceRoles(getSession()).purge(null);
+
+ checkTables("purgeResourceRoles", "projects", "user_roles", "group_roles");
+ }
+}
+
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeRuleMeasuresTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeRuleMeasuresTest.java
new file mode 100644
index 00000000000..76ae0121867
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeRuleMeasuresTest.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.plugins.core.purges;
+
+import org.junit.Test;
+import org.sonar.api.batch.PurgeContext;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+public class PurgeRuleMeasuresTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void purgeRuleMeasures() {
+ setupData("sharedFixture", "purgeRuleMeasures");
+
+ new PurgeRuleMeasures(getSession()).purge(new PurgeContext(){
+ public Integer getPreviousSnapshotId() {
+ return 1;
+ }
+ public Integer getLastSnapshotId() {
+ return 4;
+ }
+ });
+
+ checkTables("purgeRuleMeasures", "snapshots", "project_measures", "measure_data");
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeUnprocessedTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeUnprocessedTest.java
new file mode 100644
index 00000000000..768098d6a00
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/PurgeUnprocessedTest.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.plugins.core.purges;
+
+import org.junit.Test;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+public class PurgeUnprocessedTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void purgeUnprocessed() {
+ setupData("sharedFixture", "purgeUnprocessed");
+
+ new PurgeUnprocessed(getSession()).purge(null);
+
+ checkTables("purgeUnprocessed", "snapshots", "project_measures", "measure_data", "rule_failures", "snapshot_sources");
+ }
+}
+
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/UnflagLastDoublonsTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/UnflagLastDoublonsTest.java
new file mode 100644
index 00000000000..e6633fc8d70
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/purges/UnflagLastDoublonsTest.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.plugins.core.purges;
+
+import org.junit.Test;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import java.sql.SQLException;
+
+public class UnflagLastDoublonsTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void unflagLastDoublons() throws SQLException {
+ setupData("sharedFixture", "unflagLastDoublons");
+
+ new UnflagLastDoublons(getSession()).purge(null);
+
+ checkTables("unflagLastDoublons", "snapshots");
+ }
+}
+
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/security/ApplyProjectRolesDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/security/ApplyProjectRolesDecoratorTest.java
new file mode 100644
index 00000000000..51673cd37f0
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/security/ApplyProjectRolesDecoratorTest.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.plugins.core.security;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.resources.Project;
+import org.sonar.api.security.GroupRole;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import static org.mockito.Mockito.*;
+
+public class ApplyProjectRolesDecoratorTest {
+
+ private RoleManager roleManager;
+ private ApplyProjectRolesDecorator decorator;
+
+ @Before
+ public void before() {
+ roleManager = mock(RoleManager.class);
+ decorator = new ApplyProjectRolesDecorator(roleManager);
+ }
+
+ @Test
+ public void doNotApplySecurityWhenExistingRoles() {
+ Project project = new Project("project");
+ project.setId(10);
+ when(roleManager.getGroupRoles(10)).thenReturn(Arrays.<GroupRole>asList(new GroupRole()));
+
+ decorator.decorate(project, null);
+
+ verify(roleManager, never()).affectDefaultRolesToResource(anyInt());
+ }
+
+ @Test
+ public void doNotApplySecurityOnModules() {
+ Project project = new Project("project");
+ Project module = new Project("module").setParent(project);
+ module.setId(10);
+
+ when(roleManager.getGroupRoles(10)).thenReturn(Arrays.<GroupRole>asList());
+
+ decorator.decorate(module, null);
+
+ verify(roleManager, never()).affectDefaultRolesToResource(anyInt());
+ }
+
+ @Test
+ public void applySecurityWhenNoRoles() {
+ Project project = new Project("project");
+ project.setId(10);
+ when(roleManager.getGroupRoles(10)).thenReturn(new ArrayList<GroupRole>());
+
+ decorator.decorate(project, null);
+
+ verify(roleManager).affectDefaultRolesToResource(10);
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/security/RoleManagerTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/security/RoleManagerTest.java
new file mode 100644
index 00000000000..ad89b5611b7
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/security/RoleManagerTest.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.plugins.core.security;
+
+import org.junit.Test;
+import org.sonar.jpa.test.AbstractDbUnitTestCase;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+
+public class RoleManagerTest extends AbstractDbUnitTestCase {
+
+ @Test
+ public void affectDefaultRolesToResource() {
+ setupData("affectDefaultRolesToResource");
+ new RoleManager(getSession()).affectDefaultRolesToResource(10);
+ checkTables("affectDefaultRolesToResource", "user_roles", "group_roles");
+ }
+
+ @Test
+ public void affectZeroDefaultRolesToResource() {
+ setupData("affectZeroDefaultRolesToResource");
+ new RoleManager(getSession()).affectDefaultRolesToResource(10);
+ checkTables("affectZeroDefaultRolesToResource", "user_roles", "group_roles");
+ }
+
+ @Test
+ public void affectAnyoneDefaultRoleToResource() {
+ setupData("affectAnyoneDefaultRoleToResource");
+ new RoleManager(getSession()).affectDefaultRolesToResource(10);
+ checkTables("affectAnyoneDefaultRoleToResource", "group_roles");
+ }
+
+ @Test
+ public void convertDefaultRoleName() {
+ assertThat(RoleManager.convertDefaultRoleName(RoleManager.DEFAULT_ROLE_PREFIX + "admin"), is("admin"));
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/AlertUtilsTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/AlertUtilsTest.java
new file mode 100644
index 00000000000..828ab46c4c9
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/AlertUtilsTest.java
@@ -0,0 +1,253 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.sensors;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.profiles.Alert;
+
+public class AlertUtilsTest {
+ private Metric metric;
+ private Measure measure;
+ private Metric metric2;
+ private Measure measure2;
+ private Alert alert;
+
+ @Before
+ public void setup() {
+ metric = new Metric("test-metric");
+ measure = new Measure();
+ measure.setMetric(metric);
+
+ metric2 = new Metric("test-metric2");
+ measure2 = new Measure();
+ measure2.setMetric(metric2);
+
+ alert = new Alert();
+ }
+
+ @Test
+ public void testInputNumbers() {
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_SMALLER);
+ alert.setMetric(metric);
+
+ try {
+ metric.setType(Metric.ValueType.FLOAT);
+ alert.setValueError("20");
+ AlertUtils.getLevel(alert, measure);
+ } catch (NumberFormatException ex) {
+ Assert.fail();
+ }
+
+ try {
+ metric.setType(Metric.ValueType.INT);
+ alert.setValueError("20.1");
+ AlertUtils.getLevel(alert, measure);
+ } catch (NumberFormatException ex) {
+ Assert.fail();
+ }
+
+ try {
+ metric.setType(Metric.ValueType.PERCENT);
+ alert.setValueError("20.1");
+ AlertUtils.getLevel(alert, measure);
+ } catch (NumberFormatException ex) {
+ Assert.fail();
+ }
+ }
+
+ @Test
+ public void testEquals() {
+
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_EQUALS);
+ alert.setMetric(metric);
+
+ alert.setValueError("10.2");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("10.1");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+
+ metric.setType(Metric.ValueType.STRING);
+ measure.setData("TEST");
+ measure.setValue(null);
+
+ alert.setValueError("TEST");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("TEST2");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+
+ }
+
+ @Test
+ public void testNotEquals() {
+
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_NOT_EQUALS);
+ alert.setMetric(metric);
+
+ alert.setValueError("10.2");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("10.1");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ metric.setType(Metric.ValueType.STRING);
+ measure.setData("TEST");
+ measure.setValue(null);
+
+ alert.setValueError("TEST");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("TEST2");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ }
+
+ @Test
+ public void testGreater() {
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_GREATER);
+ alert.setMetric(metric);
+
+ alert.setValueError("10.1");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("10.3");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+ }
+
+ @Test
+ public void testSmaller() {
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_SMALLER);
+ alert.setMetric(metric);
+
+ alert.setValueError("10.1");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("10.3");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+ }
+
+ @Test
+ public void testPercent() {
+ metric.setType(Metric.ValueType.PERCENT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_EQUALS);
+ alert.setMetric(metric);
+
+ alert.setValueError("10.2");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+ }
+
+ @Test
+ public void testFloat() {
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_EQUALS);
+ alert.setMetric(metric);
+
+ alert.setValueError("10.2");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+ }
+
+ @Test
+ public void testInteger() {
+ metric.setType(Metric.ValueType.INT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_EQUALS);
+ alert.setMetric(metric);
+
+ alert.setValueError("10");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("10.2");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+ }
+
+ @Test
+ public void testLevel() {
+ metric.setType(Metric.ValueType.LEVEL);
+ measure.setData(Metric.Level.ERROR.toString());
+ alert.setOperator(Alert.OPERATOR_EQUALS);
+ alert.setMetric(metric);
+
+ alert.setValueError(Metric.Level.ERROR.toString());
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError(Metric.Level.OK.toString());
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+
+ alert.setOperator(Alert.OPERATOR_NOT_EQUALS);
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+ }
+
+ @Test
+ public void testBooleans() {
+ metric.setType(Metric.ValueType.BOOL);
+ measure.setValue(0d);
+ alert.setOperator(Alert.OPERATOR_EQUALS);
+ alert.setMetric(metric);
+
+ alert.setValueError("true");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("false");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ alert.setOperator(Alert.OPERATOR_NOT_EQUALS);
+ alert.setValueError("true");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("false");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+ }
+
+ @Test
+ public void testErrorAndWarningLevel() {
+
+ metric.setType(Metric.ValueType.FLOAT);
+ measure.setValue(10.2d);
+ alert.setOperator(Alert.OPERATOR_EQUALS);
+ alert.setMetric(metric);
+
+ alert.setValueError("10.2");
+ Assert.assertEquals(Metric.Level.ERROR, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("10.1");
+ Assert.assertEquals(Metric.Level.OK, AlertUtils.getLevel(alert, measure));
+
+ alert.setValueError("10.3");
+ alert.setValueWarning("10.2");
+ Assert.assertEquals(Metric.Level.WARN, AlertUtils.getLevel(alert, measure));
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/BranchCoverageDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/BranchCoverageDecoratorTest.java
new file mode 100644
index 00000000000..69e3e06bd9e
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/BranchCoverageDecoratorTest.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.plugins.core.sensors;
+
+import org.junit.Test;
+import static org.mockito.Matchers.anyDouble;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.*;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.Project;
+
+public class BranchCoverageDecoratorTest {
+
+ @Test
+ public void noBranchCoverageIfMissingConditions() {
+ Project resource = mock(Project.class);
+ when(resource.getScope()).thenReturn(Project.SCOPE_SET);
+ when(resource.getQualifier()).thenReturn(Project.QUALIFIER_SUBVIEW);
+
+ DecoratorContext context = mockContext(null, null);
+ new BranchCoverageDecorator().decorate(resource, context);
+
+ verify(context, never()).saveMeasure(eq(CoreMetrics.BRANCH_COVERAGE), anyDouble());
+ }
+
+ @Test
+ public void branchCoverage() {
+ Project resource = mock(Project.class);
+ when(resource.getScope()).thenReturn(Project.SCOPE_SET);
+ when(resource.getQualifier()).thenReturn(Project.QUALIFIER_PROJECT);
+
+ DecoratorContext context = mockContext(20, 15);
+
+ new BranchCoverageDecorator().decorate(resource, context);
+ verify(context).saveMeasure(CoreMetrics.BRANCH_COVERAGE, 25.0);
+ }
+
+
+ private DecoratorContext mockContext(Integer conditions, Integer uncoveredConditions) {
+ DecoratorContext context = mock(DecoratorContext.class);
+ if (conditions != null) {
+ when(context.getMeasure(CoreMetrics.CONDITIONS_TO_COVER)).thenReturn(new Measure(CoreMetrics.CONDITIONS_TO_COVER, conditions.doubleValue()));
+ }
+ if (uncoveredConditions != null) {
+ when(context.getMeasure(CoreMetrics.UNCOVERED_CONDITIONS)).thenReturn(new Measure(CoreMetrics.UNCOVERED_CONDITIONS, uncoveredConditions.doubleValue()));
+ }
+ return context;
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CheckAlertThresholdsTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CheckAlertThresholdsTest.java
new file mode 100644
index 00000000000..4354c4914c9
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CheckAlertThresholdsTest.java
@@ -0,0 +1,167 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.sensors;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.profiles.Alert;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.test.IsMeasure;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.*;
+
+public class CheckAlertThresholdsTest {
+ private CheckAlertThresholds decorator;
+ private DecoratorContext context;
+ private RulesProfile profile;
+ private Measure measureClasses;
+ private Measure measureCoverage;
+ private Resource project;
+
+
+ @Before
+ public void setup() {
+ context = mock(DecoratorContext.class);
+
+ measureClasses = new Measure(CoreMetrics.CLASSES, 20d);
+ measureCoverage = new Measure(CoreMetrics.COVERAGE, 35d);
+
+ when(context.getMeasure(CoreMetrics.CLASSES)).thenReturn(measureClasses);
+ when(context.getMeasure(CoreMetrics.COVERAGE)).thenReturn(measureCoverage);
+
+ profile = mock(RulesProfile.class);
+ decorator = new CheckAlertThresholds(profile);
+ project = mock(Resource.class);
+ when(project.getQualifier()).thenReturn(Project.QUALIFIER_PROJECT);
+ }
+
+ @Test
+ public void shouldNotCreateAlertsWhenNoThresholds() {
+ when(profile.getAlerts()).thenReturn(new ArrayList<Alert>());
+ assertFalse(decorator.shouldExecuteOnProject(new Project("key")));
+ }
+
+ @Test
+ public void shouldBeOkWhenNoAlerts() {
+ when(profile.getAlerts()).thenReturn(Arrays.asList(
+ new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "20"),
+ new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_GREATER, null, "35.0")));
+
+ decorator.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.ALERT_STATUS, Metric.Level.OK.toString())));
+ verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
+ verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.OK)));
+ }
+
+ @Test
+ public void checkRootProjectsOnly() {
+ when(project.getQualifier()).thenReturn(Project.QUALIFIER_FILE);
+ when(profile.getAlerts()).thenReturn(Arrays.asList(
+ new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "20"),
+ new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_GREATER, null, "35.0")));
+
+ decorator.decorate(project, context);
+
+ verify(context, never()).saveMeasure((Measure) anyObject());
+ }
+
+ @Test
+ public void shouldGenerateWarnings() {
+ when(profile.getAlerts()).thenReturn(Arrays.asList(
+ new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_GREATER, null, "100"),
+ new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_SMALLER, null, "95.0"))); // generates warning because coverage 35% < 95%
+
+ decorator.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.WARN, null)));
+
+ verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.OK)));
+ verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.WARN)));
+
+ }
+
+ @Test
+ public void globalStatusShouldBeErrorIfWarningsAndErrors() {
+ when(profile.getAlerts()).thenReturn(Arrays.asList(
+ new Alert(null, CoreMetrics.CLASSES, Alert.OPERATOR_SMALLER, null, "100"), // generates warning because classes 20 < 100
+ new Alert(null, CoreMetrics.COVERAGE, Alert.OPERATOR_SMALLER, "50.0", "80.0"))); // generates error because coverage 35% < 50%
+
+ decorator.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, null)));
+
+ verify(context).saveMeasure(argThat(hasLevel(measureClasses, Metric.Level.WARN)));
+ verify(context).saveMeasure(argThat(hasLevel(measureCoverage, Metric.Level.ERROR)));
+ }
+
+ @Test
+ public void globalLabelShouldAggregateAllLabels() {
+ Alert alert1 = mock(Alert.class);
+ when(alert1.getMetric()).thenReturn(CoreMetrics.CLASSES);
+ when(alert1.getValueError()).thenReturn("10000"); // there are 20 classes, error threshold is higher => alert
+ when(alert1.getAlertLabel(Metric.Level.ERROR)).thenReturn("error classes");
+
+ Alert alert2 = mock(Alert.class);
+ when(alert2.getMetric()).thenReturn(CoreMetrics.COVERAGE);
+ when(alert2.getValueWarning()).thenReturn("80"); // coverage is 35%, warning threshold is higher => alert
+ when(alert2.getAlertLabel(Metric.Level.WARN)).thenReturn("warning coverage");
+
+ when(profile.getAlerts()).thenReturn(Arrays.asList(alert1, alert2));
+ decorator.decorate(project, context);
+
+ verify(context).saveMeasure(argThat(matchesMetric(CoreMetrics.ALERT_STATUS, Metric.Level.ERROR, "error classes, warning coverage")));
+ }
+
+ private ArgumentMatcher<Measure> matchesMetric(final Metric metric, final Metric.Level alertStatus, final String alertText) {
+ return new ArgumentMatcher<Measure>() {
+ @Override
+ public boolean matches(Object arg) {
+ boolean result = ((Measure) arg).getMetric().equals(metric) && ((Measure) arg).getAlertStatus() == alertStatus;
+ if (result && alertText != null) {
+ result = alertText.equals(((Measure) arg).getAlertText());
+ }
+ return result;
+ }
+ };
+ }
+
+ private ArgumentMatcher<Measure> hasLevel(final Measure measure, final Metric.Level alertStatus) {
+ return new ArgumentMatcher<Measure>() {
+ @Override
+ public boolean matches(Object arg) {
+ return arg == measure && ((Measure) arg).getAlertStatus().equals(alertStatus);
+ }
+ };
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CommentDensityDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CommentDensityDecoratorTest.java
new file mode 100644
index 00000000000..fd887e5df7a
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CommentDensityDecoratorTest.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.plugins.core.sensors;
+
+import org.junit.Test;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.test.IsMeasure;
+
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class CommentDensityDecoratorTest {
+
+ @Test
+ public void densityIsBalancedByNcloc() {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 300.0));
+ when(context.getMeasure(CoreMetrics.COMMENT_LINES)).thenReturn(new Measure(CoreMetrics.COMMENT_LINES, 200.0));
+ CommentDensityDecorator decorator = new CommentDensityDecorator();
+ decorator.decorate(null, context);
+ // 200 / (200 + 300) = 40%
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.COMMENT_LINES_DENSITY, 40.0)));
+ }
+
+ @Test
+ public void noDensityIfUnknownComments() {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 300.0));
+ CommentDensityDecorator decorator = new CommentDensityDecorator();
+ decorator.decorate(null, context);
+ verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.COMMENT_LINES_DENSITY)));
+ }
+
+ @Test
+ public void noDensityIfZeroNcloc() {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 0.0));
+ when(context.getMeasure(CoreMetrics.COMMENT_LINES)).thenReturn(new Measure(CoreMetrics.COMMENT_LINES, 0.0));
+ CommentDensityDecorator decorator = new CommentDensityDecorator();
+ decorator.decorate(null, context);
+ verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.COMMENT_LINES_DENSITY)));
+ }
+
+ @Test
+ public void zeroDensityWhenZeroComments() {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 40.0));
+ when(context.getMeasure(CoreMetrics.COMMENT_LINES)).thenReturn(new Measure(CoreMetrics.COMMENT_LINES, 0.0));
+ CommentDensityDecorator decorator = new CommentDensityDecorator();
+ decorator.decorate(null, context);
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.COMMENT_LINES_DENSITY, 0.0)));
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CoverageDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CoverageDecoratorTest.java
new file mode 100644
index 00000000000..01a90d7afb0
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/CoverageDecoratorTest.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.plugins.core.sensors;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import org.junit.Before;
+import org.junit.Test;
+import static org.mockito.Mockito.*;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.Project;
+
+public class CoverageDecoratorTest {
+
+ private Project project = null;
+
+ @Before
+ public void before() {
+ project = mock(Project.class);
+ when(project.getScope()).thenReturn(Project.SCOPE_SET);
+ }
+
+ @Test
+ public void noCoverageWhenStaticAnalysis() {
+ when(project.getAnalysisType()).thenReturn(Project.AnalysisType.STATIC);
+ assertThat(new CoverageDecorator().shouldExecuteOnProject(project), is(false));
+
+ when(project.getAnalysisType()).thenReturn(Project.AnalysisType.REUSE_REPORTS);
+ assertThat(new CoverageDecorator().shouldExecuteOnProject(project), is(true));
+
+ when(project.getAnalysisType()).thenReturn(Project.AnalysisType.DYNAMIC);
+ assertThat(new CoverageDecorator().shouldExecuteOnProject(project), is(true));
+ }
+
+ @Test
+ public void coverage() {
+ DecoratorContext context = mockContext(50, 40, 10, 8);
+
+ new CoverageDecorator().decorate(project, context);
+
+ // (50-40 covered lines + 10-8 covered conditions) / (50 lines + 10 conditions)
+ verify(context).saveMeasure(CoreMetrics.COVERAGE, 20.0);
+ }
+
+ @Test
+ public void coverageCanBe0() {
+ DecoratorContext context = mockContext(50, 50, 5, 5);
+ new CoverageDecorator().decorate(project, context);
+
+ verify(context).saveMeasure(CoreMetrics.COVERAGE, 0.0);
+ }
+
+ @Test
+ public void coverageCanBe100() {
+ DecoratorContext context = mockContext(50, 0, 5, 0);
+ new CoverageDecorator().decorate(project, context);
+
+ verify(context).saveMeasure(CoreMetrics.COVERAGE, 100.0);
+ }
+
+ @Test
+ public void noCoverageIfNoElements() {
+ DecoratorContext context = mockContext(null, null, null, null);
+ new CoverageDecorator().decorate(project, context);
+
+ verify(context, never()).saveMeasure(eq(CoreMetrics.COVERAGE), anyDouble());
+ }
+
+ private DecoratorContext mockContext(Integer lines, Integer uncoveredLines, Integer conditions, Integer uncoveredConditions) {
+ DecoratorContext context = mock(DecoratorContext.class);
+ if (lines != null) {
+ when(context.getMeasure(CoreMetrics.LINES_TO_COVER)).thenReturn(new Measure(CoreMetrics.LINES_TO_COVER, lines.doubleValue()));
+ }
+ if (uncoveredLines != null) {
+ when(context.getMeasure(CoreMetrics.UNCOVERED_LINES)).thenReturn(new Measure(CoreMetrics.UNCOVERED_LINES, uncoveredLines.doubleValue()));
+ }
+ if (conditions != null) {
+ when(context.getMeasure(CoreMetrics.CONDITIONS_TO_COVER)).thenReturn(new Measure(CoreMetrics.CONDITIONS_TO_COVER, conditions.doubleValue()));
+ }
+ if (uncoveredConditions != null) {
+ when(context.getMeasure(CoreMetrics.UNCOVERED_CONDITIONS)).thenReturn(new Measure(CoreMetrics.UNCOVERED_CONDITIONS, uncoveredConditions.doubleValue()));
+ }
+ return context;
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/DirectoriesDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/DirectoriesDecoratorTest.java
new file mode 100644
index 00000000000..b3b3720fb5f
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/DirectoriesDecoratorTest.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.plugins.core.sensors;
+
+import org.junit.Test;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.*;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.mockito.Matchers.anyDouble;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.*;
+
+public class DirectoriesDecoratorTest {
+
+ @Test
+ public void doNotInsertZeroOnFiles() {
+ DirectoriesDecorator decorator = new DirectoriesDecorator();
+ Resource file = new File("foo.php");
+ DecoratorContext context = mock(DecoratorContext.class);
+
+ decorator.decorate(file, context);
+
+ verify(context, never()).saveMeasure(eq(CoreMetrics.DIRECTORIES), anyDouble());
+ }
+
+ @Test
+ public void directoryCountsForOne() {
+ DirectoriesDecorator decorator = new DirectoriesDecorator();
+ Resource directory = new Directory("org/foo");
+ DecoratorContext context = mock(DecoratorContext.class);
+ decorator.decorate(directory, context);
+ verify(context).saveMeasure(CoreMetrics.DIRECTORIES, 1.0);
+ }
+
+ @Test
+ public void countProjectDirectories() {
+ DirectoriesDecorator decorator = new DirectoriesDecorator();
+ Resource project = new Project("project");
+ DecoratorContext context = mock(DecoratorContext.class);
+
+ when(context.getChildrenMeasures(CoreMetrics.DIRECTORIES)).thenReturn(Arrays.<Measure>asList(
+ new Measure(CoreMetrics.DIRECTORIES, 1.0),
+ new Measure(CoreMetrics.DIRECTORIES, 1.0),
+ new Measure(CoreMetrics.DIRECTORIES, 1.0)
+ ));
+ decorator.decorate(project, context);
+ verify(context).saveMeasure(CoreMetrics.DIRECTORIES, 3.0);
+ }
+
+ @Test
+ public void packagesAreIgnored() {
+ DirectoriesDecorator decorator = new DirectoriesDecorator();
+ Resource pac = new JavaPackage("org.foo");
+ DecoratorContext context = mock(DecoratorContext.class);
+ decorator.decorate(pac, context);
+ verify(context, never()).saveMeasure(eq(CoreMetrics.DIRECTORIES), anyDouble());
+ }
+
+ @Test
+ public void noProjectValueWhenOnlyPackages() {
+ DirectoriesDecorator decorator = new DirectoriesDecorator();
+ Resource project = new Project("project");
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getChildrenMeasures(CoreMetrics.DIRECTORIES)).thenReturn(Collections.<Measure>emptyList());
+ when(context.getChildrenMeasures(CoreMetrics.PACKAGES)).thenReturn(Arrays.<Measure>asList(
+ new Measure(CoreMetrics.PACKAGES, 1.0),
+ new Measure(CoreMetrics.PACKAGES, 1.0)
+ ));
+ decorator.decorate(project, context);
+ verify(context, never()).saveMeasure(eq(CoreMetrics.DIRECTORIES), anyDouble());
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/GenerateAlertEventsTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/GenerateAlertEventsTest.java
new file mode 100644
index 00000000000..6a5732d76ae
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/GenerateAlertEventsTest.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.plugins.core.sensors;
+
+import org.dbunit.dataset.DataSetException;
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import org.junit.Before;
+import org.junit.Test;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.*;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.Event;
+import org.sonar.api.batch.TimeMachine;
+import org.sonar.api.batch.TimeMachineQuery;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.Metric;
+import org.sonar.api.profiles.Alert;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.resources.Project;
+import org.sonar.api.test.ProjectTestBuilder;
+
+import java.util.Arrays;
+import java.util.Date;
+
+public class GenerateAlertEventsTest {
+ private GenerateAlertEvents decorator;
+ private DecoratorContext context;
+ private RulesProfile profile;
+ private TimeMachine timeMachine;
+ private Project project;
+
+ @Before
+ public void setup() {
+ context = mock(DecoratorContext.class);
+ timeMachine = mock(TimeMachine.class);
+ profile = mock(RulesProfile.class);
+ decorator = new GenerateAlertEvents(profile, timeMachine);
+ project = new ProjectTestBuilder().build();
+ }
+
+ @Test
+ public void doNotDecorateIfNoThresholds() {
+ assertThat(decorator.shouldExecuteOnProject(project), is(false));
+ }
+
+ @Test
+ public void shouldDecorateIfThresholds() {
+ when(profile.getAlerts()).thenReturn(Arrays.asList(new Alert()));
+ assertThat(decorator.shouldExecuteOnProject(project), is(true));
+ }
+
+ @Test
+ public void shouldCreateEventWhenNewErrorAlert() {
+ when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.ERROR, "desc"));
+ decorator.decorate(project, context);
+ verify(context).createEvent(Metric.Level.ERROR.getColorName(), "desc", Event.CATEGORY_ALERT, null);
+ }
+
+ @Test
+ public void shouldCreateEventWhenNewWarnAlert() {
+ when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.WARN, "desc"));
+ decorator.decorate(project, context);
+ verify(context).createEvent(Metric.Level.WARN.getColorName(), "desc", Event.CATEGORY_ALERT, null);
+ }
+
+ @Test
+ public void shouldCreateEventWhenWarnToError() {
+ when(timeMachine.getMeasures((TimeMachineQuery) anyObject())).thenReturn(Arrays.asList(newAlertStatus(Metric.Level.WARN, "desc")));
+ when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.ERROR, "desc"));
+
+ decorator.decorate(project, context);
+
+ verify(context).createEvent("Red (was Orange)", "desc", Event.CATEGORY_ALERT, null);
+ }
+
+ @Test
+ public void shouldCreateEventWhenErrorToOk() {
+ when(timeMachine.getMeasures((TimeMachineQuery) anyObject())).thenReturn(Arrays.asList(newAlertStatus(Metric.Level.ERROR, "desc")));
+ when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.OK, null));
+
+ decorator.decorate(project, context);
+
+ verify(context).createEvent("Green (was Red)", null, Event.CATEGORY_ALERT, null);
+ }
+
+ @Test
+ public void shouldCreateEventWhenErrorToWarn() {
+ when(timeMachine.getMeasures((TimeMachineQuery) anyObject())).thenReturn(Arrays.asList(newAlertStatus(Metric.Level.ERROR, "desc")));
+ when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.WARN, "desc"));
+
+ decorator.decorate(project, context);
+
+ verify(context).createEvent("Orange (was Red)", "desc", Event.CATEGORY_ALERT, null);
+ }
+
+ @Test
+ public void shouldNotCreateEventWhenNoAlertStatus() throws DataSetException {
+ decorator.decorate(project, context);
+
+ verify(context, never()).createEvent(anyString(), anyString(), anyString(), (Date) isNull());
+ }
+
+ @Test
+ public void shouldNotCreateEventWhenSameLevel() throws DataSetException {
+ when(timeMachine.getMeasures((TimeMachineQuery) anyObject())).thenReturn(Arrays.asList(newAlertStatus(Metric.Level.ERROR, "desc")));
+ when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(newAlertStatus(Metric.Level.ERROR, "desc"));
+
+ decorator.decorate(project, context);
+
+ verify(context, never()).createEvent(anyString(), anyString(), anyString(), (Date) isNull());
+ }
+
+ @Test
+ public void shouldNotCreateEventIfNoMoreAlertStatus() throws DataSetException {
+ when(timeMachine.getMeasures((TimeMachineQuery) anyObject())).thenReturn(Arrays.asList(newAlertStatus(Metric.Level.ERROR, "desc")));
+ when(context.getMeasure(CoreMetrics.ALERT_STATUS)).thenReturn(null);
+
+ decorator.decorate(project, context);
+
+ verify(context, never()).createEvent(anyString(), anyString(), anyString(), (Date) isNull());
+ }
+
+
+ private Measure newAlertStatus(Metric.Level level, String label) {
+ Measure measure = new Measure(CoreMetrics.ALERT_STATUS, level);
+ measure.setAlertStatus(level);
+ measure.setAlertText(label);
+ return measure;
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/JavaSourceImporterTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/JavaSourceImporterTest.java
new file mode 100644
index 00000000000..34927cfb1c7
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/JavaSourceImporterTest.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.plugins.core.sensors;
+
+import org.apache.commons.io.FileUtils;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+import org.junit.Test;
+import org.sonar.api.resources.JavaFile;
+import org.sonar.api.resources.JavaPackage;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.resources.ResourceUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+
+public class JavaSourceImporterTest {
+
+ @Test
+ public void shouldCreateResource() throws IOException {
+ JavaSourceImporter importer = new JavaSourceImporter();
+ Resource clazz = importer.createResource(new File(newDir("source1"), "/MyClass.java"), Arrays.asList(newDir("source1")), false);
+ assertThat(clazz, is(JavaFile.class));
+ assertThat(clazz.getKey(), is(JavaPackage.DEFAULT_PACKAGE_NAME + ".MyClass"));
+ assertThat(clazz.getName(), is("MyClass"));
+ }
+
+ @Test
+ public void shouldCreateTestResource() throws IOException {
+ JavaSourceImporter importer = new JavaSourceImporter();
+ Resource resource = importer.createResource(new File(newDir("tests"), "/MyClassTest.java"), Arrays.asList(newDir("tests")), true);
+ assertThat(resource, is(JavaFile.class));
+ assertThat(ResourceUtils.isUnitTestClass(resource), is(true));
+ }
+
+ @Test
+ public void doNotSaveInnerClasses() throws IOException {
+ // example : https://svn.apache.org/repos/asf/geronimo/server/trunk/plugins/corba/geronimo-corba/src/test/java/org/apache/geronimo/corba/compiler/other/Generic$Interface.java
+ JavaSourceImporter importer = new JavaSourceImporter();
+ Resource resource = importer.createResource(new File(newDir("tests"), "/Generic$Interface.java"), Arrays.asList(newDir("tests")), true);
+ assertThat(resource, nullValue());
+ }
+
+ private File newDir(String relativePath) throws IOException {
+ File target = new File("target", relativePath);
+ FileUtils.forceMkdir(target);
+ return target;
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/LineCoverageDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/LineCoverageDecoratorTest.java
new file mode 100644
index 00000000000..018ac637503
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/LineCoverageDecoratorTest.java
@@ -0,0 +1,99 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.sensors;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import org.junit.Before;
+import org.junit.Test;
+import static org.mockito.Mockito.*;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.Project;
+
+public class LineCoverageDecoratorTest {
+
+ private Project project = null;
+
+ @Before
+ public void before() {
+ project = mock(Project.class);
+ when(project.getScope()).thenReturn(Project.SCOPE_SET);
+ }
+
+ @Test
+ public void noCoverageWhenStaticAnalysis() {
+ when(project.getAnalysisType()).thenReturn(Project.AnalysisType.STATIC);
+ assertThat(new LineCoverageDecorator().shouldExecuteOnProject(project), is(false));
+
+ when(project.getAnalysisType()).thenReturn(Project.AnalysisType.REUSE_REPORTS);
+ assertThat(new LineCoverageDecorator().shouldExecuteOnProject(project), is(true));
+
+ when(project.getAnalysisType()).thenReturn(Project.AnalysisType.DYNAMIC);
+ assertThat(new LineCoverageDecorator().shouldExecuteOnProject(project), is(true));
+ }
+
+ @Test
+ public void lineCoverage() {
+ DecoratorContext context = mockContext(50, 10);
+ new LineCoverageDecorator().decorate(project, context);
+
+ // 50-10 covered lines / 50 lines
+ verify(context).saveMeasure(CoreMetrics.LINE_COVERAGE, 80.0);
+ }
+
+
+ @Test
+ public void noLineCoverageIfNoLines() {
+ DecoratorContext context = mockContext(null, null);
+ new LineCoverageDecorator().decorate(project, context);
+
+ verify(context, never()).saveMeasure(eq(CoreMetrics.LINE_COVERAGE), anyDouble());
+ }
+
+ @Test
+ public void zeroCoveredLines() {
+ DecoratorContext context = mockContext(50, 50);
+ new LineCoverageDecorator().decorate(project, context);
+
+ verify(context).saveMeasure(CoreMetrics.LINE_COVERAGE, 0.0);
+ }
+
+ @Test
+ public void allCoveredLines() {
+ DecoratorContext context = mockContext(50, 00);
+ new LineCoverageDecorator().decorate(project, context);
+
+ verify(context).saveMeasure(CoreMetrics.LINE_COVERAGE, 100.0);
+ }
+
+ private DecoratorContext mockContext(Integer lines, Integer uncoveredLines) {
+ DecoratorContext context = mock(DecoratorContext.class);
+ if (lines != null) {
+ when(context.getMeasure(CoreMetrics.LINES_TO_COVER)).thenReturn(new Measure(CoreMetrics.LINES_TO_COVER, lines.doubleValue()));
+ }
+ if (uncoveredLines != null) {
+ when(context.getMeasure(CoreMetrics.UNCOVERED_LINES)).thenReturn(new Measure(CoreMetrics.UNCOVERED_LINES, uncoveredLines.doubleValue()));
+ }
+ return context;
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ProfileSensorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ProfileSensorTest.java
new file mode 100644
index 00000000000..c0b94847950
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ProfileSensorTest.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.plugins.core.sensors;
+
+import org.junit.Test;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.*;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.profiles.RulesProfile;
+import org.sonar.api.test.IsMeasure;
+
+public class ProfileSensorTest {
+
+ @Test
+ public void saveProfile() {
+ RulesProfile profile = mock(RulesProfile.class);
+ when(profile.getId()).thenReturn(22);
+ when(profile.getName()).thenReturn("fake");
+ SensorContext context = mock(SensorContext.class);
+
+ ProfileSensor sensor = new ProfileSensor(profile);
+ sensor.analyse(null, context);
+
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.PROFILE, 22d)));
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ProjectLinksSensorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ProjectLinksSensorTest.java
new file mode 100644
index 00000000000..69b90dc466e
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ProjectLinksSensorTest.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.plugins.core.sensors;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.maven.project.MavenProject;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.junit.Test;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Mockito.*;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.ProjectLink;
+import org.sonar.api.test.MavenTestUtils;
+
+public class ProjectLinksSensorTest {
+
+ @Test
+ public void shouldSaveLinks() {
+ SensorContext context = mock(SensorContext.class);
+ MavenProject pom = MavenTestUtils.loadPom("/org/sonar/plugins/core/sensors/ProjectLinksSensorTest/shouldSaveLinks.xml");
+ Project project = mock(Project.class);
+ when(project.getPom()).thenReturn(pom);
+
+ new ProjectLinksSensor().analyse(project, context);
+
+ verify(context).saveLink(argThat(new MatchLink(ProjectLinksSensor.KEY_HOME, "Home", "http://sonar.codehaus.org")));
+ verify(context).saveLink(argThat(new MatchLink(ProjectLinksSensor.KEY_ISSUE_TRACKER, "Issues", "http://jira.codehaus.org/browse/SONAR")));
+ verify(context).saveLink(argThat(new MatchLink(ProjectLinksSensor.KEY_CONTINUOUS_INTEGRATION, "Continuous integration", "http://bamboo.ci.codehaus.org/browse/SONAR/")));
+ verify(context).saveLink(argThat(new MatchLink(ProjectLinksSensor.KEY_SCM, "Sources", "http://svn.sonar.codehaus.org")));
+ verify(context).saveLink(argThat(new MatchLink(ProjectLinksSensor.KEY_SCM_DEVELOPER_CONNECTION, "Developer connection", "scm:svn:https://svn.codehaus.org/sonar/trunk")));
+ }
+
+ @Test
+ public void shouldDeleteMissingLinks() {
+ SensorContext context = mock(SensorContext.class);
+ MavenProject pom = MavenTestUtils.loadPom("/org/sonar/plugins/core/sensors/ProjectLinksSensorTest/shouldDeleteMissingLinks.xml");
+ Project project = mock(Project.class);
+ when(project.getPom()).thenReturn(pom);
+
+ new ProjectLinksSensor().analyse(project, context);
+
+ verify(context).deleteLink(ProjectLinksSensor.KEY_HOME);
+ verify(context).deleteLink(ProjectLinksSensor.KEY_ISSUE_TRACKER);
+ verify(context).deleteLink(ProjectLinksSensor.KEY_CONTINUOUS_INTEGRATION);
+ verify(context).deleteLink(ProjectLinksSensor.KEY_SCM);
+ verify(context).deleteLink(ProjectLinksSensor.KEY_SCM_DEVELOPER_CONNECTION);
+ }
+
+ private class MatchLink extends BaseMatcher<ProjectLink> {
+ private String key;
+ private String name;
+ private String url;
+
+ private MatchLink(String key, String name, String url) {
+ this.key = key;
+ this.name = name;
+ this.url = url;
+ }
+
+ public boolean matches(Object o) {
+ ProjectLink link = (ProjectLink) o;
+ return StringUtils.equals(link.getHref(), url) && StringUtils.equals(link.getKey(), key) && StringUtils.equals(link.getName(), name);
+ }
+
+ public void describeTo(Description description) {
+
+ }
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/TendencyAnalyserTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/TendencyAnalyserTest.java
new file mode 100644
index 00000000000..2bab29d3f8b
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/TendencyAnalyserTest.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.plugins.core.sensors;
+
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class TendencyAnalyserTest {
+ private TendencyAnalyser analyser = new TendencyAnalyser();
+
+ private List<Double> getValues(Double[] array) {
+ return Arrays.asList(array);
+ }
+
+
+ protected void assertBetween(String typeLabel, Double value, Double min, Double max) {
+ assertTrue(typeLabel + " " + value + "<" + min, value >= min);
+ assertTrue(typeLabel + "=" + value + ">" + max, value <= max);
+ }
+
+ @Test
+ public void testNoData() {
+ assertThat(analyser.analyse(Collections.<Double>emptyList()), nullValue());
+ }
+
+ @Test
+ public void testNotEnoughData() {
+ assertThat(analyser.analyseLevel(Arrays.asList(10.0)), nullValue());
+ }
+
+ @Test
+ public void testTendencyOnThreeDays() {
+ Double[] doubles = new Double[]{10.0, null, 9.9};
+ TendencyAnalyser.SlopeData slopeData = analyser.analyse(getValues(doubles));
+ assertBetween("slope", slopeData.getSlope(), -0.5, 0.5);
+ assertEquals(TendencyAnalyser.TENDENCY_NEUTRAL, slopeData.getLevel());
+ }
+
+ @Test
+ public void testTendencyOnTwoZeroDays() {
+ Double[] doubles = new Double[]{0.0, 0.0};
+ TendencyAnalyser.SlopeData slopeData = analyser.analyse(getValues(doubles));
+ assertBetween("slope", slopeData.getSlope(), -0.0, 0.0);
+ assertEquals(TendencyAnalyser.TENDENCY_NEUTRAL, slopeData.getLevel());
+ }
+
+ @Test
+ public void testTendencyOnThreeZeroDays() {
+ Double[] doubles = new Double[]{0.0, 0.0, 0.0};
+ TendencyAnalyser.SlopeData slopeData = analyser.analyse(getValues(doubles));
+ assertBetween("slope", slopeData.getSlope(), -0.0, 0.0);
+ assertEquals(TendencyAnalyser.TENDENCY_NEUTRAL, slopeData.getLevel());
+ }
+
+ @Test
+ public void testBigDownOnThreeDays() {
+ Double[] doubles = new Double[]{90.0, 91.0, 50.0};
+ TendencyAnalyser.SlopeData slopeData = analyser.analyse(getValues(doubles));
+ assertTrue("slope", slopeData.getSlope() < -2.0);
+ assertEquals(TendencyAnalyser.TENDENCY_BIG_DOWN, slopeData.getLevel());
+ }
+
+ @Test
+ public void testFlatTendency() {
+ Double[] doubles = new Double[]{10.0, 10.2, 9.9};
+ TendencyAnalyser.SlopeData slopeData = analyser.analyse(getValues(doubles));
+ assertBetween("slope", slopeData.getSlope(), -0.5, 0.5);
+ assertEquals(TendencyAnalyser.TENDENCY_NEUTRAL, slopeData.getLevel());
+ }
+
+ @Test
+ public void testFlatTendencyWithPeak() {
+ Double[] doubles = new Double[]{10.0, 15.0, 10.0};
+ TendencyAnalyser.SlopeData slopeData = analyser.analyse(getValues(doubles));
+ assertBetween("slope", slopeData.getSlope(), -0.5, 0.5);
+ assertEquals(TendencyAnalyser.TENDENCY_NEUTRAL, slopeData.getLevel());
+ }
+
+ @Test
+ public void testBigUpTendencyOnThreeValues() {
+ Double[] doubles = new Double[]{10.0, 12.0, 15.5};
+ TendencyAnalyser.SlopeData slopeData = analyser.analyse(getValues(doubles));
+ assertBetween("slope", slopeData.getSlope(), 2.5, 3.0);
+ assertEquals(TendencyAnalyser.TENDENCY_BIG_UP, slopeData.getLevel());
+ }
+
+ @Test
+ public void testBigUpTendencyOnTenValues() {
+ Double[] doubles = new Double[]{45.0, 60.0, 57.0, 65.0, 58.0, 68.0, 59.0, 66.0, 76.0, 80.0};
+ TendencyAnalyser.SlopeData slopeData = analyser.analyse(getValues(doubles));
+ assertBetween("slope", slopeData.getSlope(), 2.5, 3.0);
+ assertEquals(TendencyAnalyser.TENDENCY_BIG_UP, slopeData.getLevel());
+ }
+
+ @Test
+ public void testMediumUpTendency() {
+ Double[] doubles = new Double[]{5.0, 4.5, 5.1, 5.5, 5.3, 6.4, 6.3, 6.6, 6.8, 6.5};
+ TendencyAnalyser.SlopeData slopeData = analyser.analyse(getValues(doubles));
+ assertBetween("slope", slopeData.getSlope(), 0.0, 1.0);
+ assertEquals(TendencyAnalyser.TENDENCY_UP, slopeData.getLevel());
+ }
+
+ @Test
+ public void testAsymetricAlgorithm() {
+ TendencyAnalyser.SlopeData slopeData1 = analyser.analyse(getValues(new Double[]{45.0, 47.0, 95.0}));
+ TendencyAnalyser.SlopeData slopeData2 = analyser.analyse(getValues(new Double[]{95.0, 45.0, 47.0}));
+ assertTrue(slopeData1.getSlope() != slopeData2.getSlope());
+ }
+} \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/TendencyDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/TendencyDecoratorTest.java
new file mode 100644
index 00000000000..7f7ddc3922d
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/TendencyDecoratorTest.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.plugins.core.sensors;
+
+import org.apache.commons.configuration.PropertiesConfiguration;
+import org.junit.Test;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.batch.TimeMachine;
+import org.sonar.api.batch.TimeMachineQuery;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.resources.JavaPackage;
+import org.sonar.api.resources.Project;
+import org.sonar.jpa.dao.MeasuresDao;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.matchers.JUnitMatchers.hasItems;
+import static org.mockito.Mockito.*;
+
+public class TendencyDecoratorTest {
+
+ @Test
+ public void shouldExecuteOnAllProjects() {
+ assertThat(new TendencyDecorator(null, null, null).shouldExecuteOnProject(null), is(true));
+ }
+
+ @Test
+ public void initQuery() throws ParseException {
+ Project project = mock(Project.class);
+ when(project.getAnalysisDate()).thenReturn(date("2009-12-25"));
+ when(project.getConfiguration()).thenReturn(new PropertiesConfiguration());
+
+ MeasuresDao dao = mock(MeasuresDao.class);
+ when(dao.getMetrics()).thenReturn(Arrays.asList(CoreMetrics.LINES, CoreMetrics.COVERAGE, CoreMetrics.COVERAGE_LINE_HITS_DATA, CoreMetrics.PROFILE));
+
+ TendencyDecorator decorator = new TendencyDecorator(null, dao);
+
+ TimeMachineQuery query = decorator.initQuery(project);
+ assertThat(query.getMetrics().size(), is(2));
+ assertThat(query.getMetrics(), hasItems(CoreMetrics.LINES, CoreMetrics.COVERAGE));
+ assertThat(query.getFrom(), is(date("2009-11-25")));
+ assertThat(query.isToCurrentAnalysis(), is(true));
+ }
+
+ @Test
+ public void includeCurrentMeasures() throws ParseException {
+ TendencyAnalyser analyser = mock(TendencyAnalyser.class);
+ TimeMachineQuery query = new TimeMachineQuery(null).setMetrics(CoreMetrics.LINES, CoreMetrics.COVERAGE);
+ TimeMachine timeMachine = mock(TimeMachine.class);
+
+ when(timeMachine.getMeasuresFields(query)).thenReturn(Arrays.<Object[]>asList(
+ new Object[]{date("2009-12-01"), CoreMetrics.LINES, 1200.0},
+ new Object[]{date("2009-12-01"), CoreMetrics.COVERAGE, 80.5},
+ new Object[]{date("2009-12-02"), CoreMetrics.LINES, 1300.0},
+ new Object[]{date("2009-12-02"), CoreMetrics.COVERAGE, 79.6},
+ new Object[]{date("2009-12-15"), CoreMetrics.LINES, 1150.0}
+ ));
+
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.LINES)).thenReturn(new Measure(CoreMetrics.LINES, 1400.0));
+ when(context.getMeasure(CoreMetrics.COVERAGE)).thenReturn(new Measure(CoreMetrics.LINES, 90.0));
+
+ TendencyDecorator decorator = new TendencyDecorator(timeMachine, query, analyser);
+ decorator.decorate(new JavaPackage("org.foo"), context);
+
+ verify(analyser).analyseLevel(Arrays.asList(1200.0, 1300.0, 1150.0, 1400.0));
+ verify(analyser).analyseLevel(Arrays.asList(80.5, 79.6, 90.0));
+ }
+
+ @Test
+ public void noTendencyIfNoCurrentMeasures() throws ParseException {
+ TendencyAnalyser analyser = mock(TendencyAnalyser.class);
+ TimeMachineQuery query = new TimeMachineQuery(null).setMetrics(CoreMetrics.LINES, CoreMetrics.COVERAGE);
+ TimeMachine timeMachine = mock(TimeMachine.class);
+
+ when(timeMachine.getMeasuresFields(query)).thenReturn(Arrays.<Object[]>asList(
+ new Object[]{date("2009-12-01"), CoreMetrics.LINES, 1200.0},
+ new Object[]{date("2009-12-02"), CoreMetrics.LINES, 1300.0}
+ ));
+
+ DecoratorContext context = mock(DecoratorContext.class);
+ TendencyDecorator decorator = new TendencyDecorator(timeMachine, query, analyser);
+ decorator.decorate(new JavaPackage("org.foo"), context);
+
+ verify(analyser, never()).analyseLevel(anyList());
+ }
+
+ private Date date(String date) throws ParseException {
+ return new SimpleDateFormat("yyyy-MM-dd").parse(date);
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/UncoveredComplexityDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/UncoveredComplexityDecoratorTest.java
new file mode 100644
index 00000000000..6c3c4f08049
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/UncoveredComplexityDecoratorTest.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.plugins.core.sensors;
+
+import static org.hamcrest.core.IsNot.not;
+import static org.hamcrest.number.OrderingComparisons.greaterThan;
+import static org.junit.Assert.assertThat;
+import org.junit.Test;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Mockito.*;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.test.IsMeasure;
+
+public class UncoveredComplexityDecoratorTest {
+
+ @Test
+ public void nothingWhenNoMeasures() {
+ DecoratorContext context = mock(DecoratorContext.class);
+ UncoveredComplexityDecorator decorator = new UncoveredComplexityDecorator();
+ decorator.decorate(null, context);
+
+ verify(context, never()).saveMeasure((Measure) anyObject());
+ }
+
+ @Test
+ public void quotientWhenMeasures() {
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.COMPLEXITY)).thenReturn(new Measure(CoreMetrics.COMPLEXITY, 1048.0));
+ when(context.getMeasure(CoreMetrics.COVERAGE)).thenReturn(new Measure(CoreMetrics.COVERAGE, 32.5));
+
+ UncoveredComplexityDecorator decorator = new UncoveredComplexityDecorator();
+ decorator.decorate(null, context);
+
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.UNCOVERED_COMPLEXITY_BY_TESTS, 707.4)));
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.UNCOVERED_COMPLEXITY_BY_TESTS, "CMP=1048;COV=32.5")));
+ }
+
+ @Test
+ public void declareDependencies() {
+ UncoveredComplexityDecorator decorator = new UncoveredComplexityDecorator();
+ assertThat(decorator.dependsUponMetrics().size(), greaterThan(0));
+ assertThat(decorator.generatesMetric(), not(isNull()));
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/UnitTestDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/UnitTestDecoratorTest.java
new file mode 100644
index 00000000000..2f837804bc0
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/UnitTestDecoratorTest.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.plugins.core.sensors;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import org.junit.Test;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import org.sonar.api.resources.Project;
+
+public class UnitTestDecoratorTest {
+
+ @Test
+ public void generatesMetrics() {
+ assertThat(new UnitTestDecorator().generatesMetrics().size(), is(5));
+ }
+
+ @Test
+ public void doNotDecorateStaticAnalysis() {
+ Project project = mock(Project.class);
+ when(project.getAnalysisType()).thenReturn(Project.AnalysisType.STATIC);
+ assertThat(new UnitTestDecorator().shouldExecuteOnProject(project), is(false));
+
+ when(project.getAnalysisType()).thenReturn(Project.AnalysisType.DYNAMIC);
+ assertThat(new UnitTestDecorator().shouldExecuteOnProject(project), is(true));
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/VersionEventsSensorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/VersionEventsSensorTest.java
new file mode 100644
index 00000000000..ef6401e5578
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/VersionEventsSensorTest.java
@@ -0,0 +1,142 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.sensors;
+
+import org.junit.Test;
+import static org.mockito.Matchers.anyObject;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.*;
+import org.sonar.api.batch.Event;
+import org.sonar.api.batch.SensorContext;
+import org.sonar.api.resources.Project;
+import org.sonar.api.resources.Resource;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+
+public class VersionEventsSensorTest {
+
+ private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
+ private SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyyMMdd HH:mm");
+
+
+ @Test
+ public void shouldDoNothingIfNoVersion() {
+ VersionEventsSensor sensor = new VersionEventsSensor();
+ SensorContext context = mock(SensorContext.class);
+ Project project = mock(Project.class);
+ when(project.getAnalysisVersion()).thenReturn(null);
+
+ sensor.analyse(project, context);
+
+ verify(context, never()).createEvent((Resource) anyObject(), anyString(), anyString(), anyString(), (Date) anyObject());
+ verify(context, never()).deleteEvent((Event) anyObject());
+ }
+
+ @Test
+ public void shouldCreateVersionEvent() {
+ VersionEventsSensor sensor = new VersionEventsSensor();
+ SensorContext context = mock(SensorContext.class);
+
+ Project project = mock(Project.class);
+ when(project.getAnalysisVersion()).thenReturn("1.5-SNAPSHOT");
+
+ sensor.analyse(project, context);
+
+ verify(context).createEvent(eq(project), eq("1.5-SNAPSHOT"), (String) isNull(), eq(Event.CATEGORY_VERSION), (Date) isNull());
+ }
+
+ @Test
+ public void shouldHaveOnlyOneEventByVersion() {
+ Event sameVersionEvent = mockVersionEvent("1.5-SNAPSHOT");
+ Event otherEvent = mockVersionEvent("1.4");
+ Event anotherEvent = mockVersionEvent("1.3-SNAPSHOT");
+
+ VersionEventsSensor sensor = new VersionEventsSensor();
+ SensorContext context = mock(SensorContext.class);
+
+ Project project = mock(Project.class);
+ when(project.getAnalysisVersion()).thenReturn("1.5-SNAPSHOT");
+
+ when(context.getEvents(project)).thenReturn(new ArrayList(Arrays.asList(sameVersionEvent, otherEvent, anotherEvent)));
+
+ sensor.analyse(project, context);
+
+ verify(context).deleteEvent(sameVersionEvent);
+ verify(context).createEvent(eq(project), eq("1.5-SNAPSHOT"), (String) isNull(), eq(Event.CATEGORY_VERSION), (Date) isNull());
+ }
+
+ @Test
+ public void shouldDeleteAssociatedSnapshotWhenReleasing() {
+ Event snapshotVersion = mockVersionEvent("1.5-SNAPSHOT");
+ Event otherEvent = mockVersionEvent("1.4");
+
+ SensorContext context = mock(SensorContext.class);
+ Project project = mock(Project.class);
+ when(project.getAnalysisVersion()).thenReturn("1.5");
+ when(context.getEvents(project)).thenReturn(new ArrayList(Arrays.asList(snapshotVersion, otherEvent)));
+
+ VersionEventsSensor sensor = new VersionEventsSensor();
+ sensor.analyse(project, context);
+
+ verify(context).deleteEvent(snapshotVersion);
+ verify(context).createEvent(eq(project), eq("1.5"), (String) isNull(), eq(Event.CATEGORY_VERSION), (Date) isNull());
+ }
+
+ @Test
+ public void shouldAssociateExistingEventsToSnapshot() throws ParseException {
+ Event christmas = mockEvent("christmas", "20081225");
+ Event newYear = mockEvent("newYear", "20090101");
+ Event valentine = mockEvent("valentine", "20090214");
+ SensorContext context = mock(SensorContext.class);
+ Project project = mock(Project.class);
+ when(project.getAnalysisVersion()).thenReturn("1.5");
+ when(project.getAnalysisDate()).thenReturn(dateTimeFormat.parse("20090101 15:34"));
+ when(context.getEvents(project)).thenReturn(new ArrayList(Arrays.asList(christmas, newYear, valentine)));
+
+ VersionEventsSensor sensor = new VersionEventsSensor();
+ sensor.analyse(project, context);
+
+ verify(context).deleteEvent(newYear);
+ verify(context).createEvent(eq(project), eq("newYear"), (String) isNull(), (String) isNull(), (Date) isNull());
+ }
+
+
+ private Event mockVersionEvent(String version) {
+ Event event = mock(Event.class);
+ when(event.isVersionCategory()).thenReturn(true);
+ when(event.getName()).thenReturn(version);
+ return event;
+ }
+
+ private Event mockEvent(String name, String yyyyMMdd) throws ParseException {
+ Event event = mock(Event.class);
+ when(event.isVersionCategory()).thenReturn(false);
+ when(event.isLinkedToSnapshot()).thenReturn(false);
+ when(event.getName()).thenReturn(name);
+ when(event.getDate()).thenReturn(dateFormat.parse(yyyyMMdd));
+ return event;
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ViolationsDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ViolationsDecoratorTest.java
new file mode 100644
index 00000000000..3ad15551926
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ViolationsDecoratorTest.java
@@ -0,0 +1,163 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.sensors;
+
+import org.junit.Before;
+import org.junit.Test;
+import static org.mockito.Mockito.*;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasuresFilter;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RulePriority;
+import org.sonar.api.rules.RulesCategory;
+import org.sonar.api.rules.Violation;
+import org.sonar.api.test.IsMeasure;
+import org.sonar.api.test.IsRuleMeasure;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class ViolationsDecoratorTest {
+ private RulesCategory categA;
+ private RulesCategory categB;
+ private Rule ruleA1;
+ private Rule ruleA2;
+ private Rule ruleB1;
+ private ViolationsDecorator decorator;
+ private Resource resource;
+ private DecoratorContext context;
+
+ @Before
+ public void before() {
+ categA = new RulesCategory("Maintainability");
+ categA.setId(1);
+
+ categB = new RulesCategory("Usability");
+ categB.setId(2);
+
+ ruleA1 = new Rule("ruleA1", "ruleA1", "nameA1", categA, null);
+ ruleA1.setId(1);
+
+ ruleA2 = new Rule("ruleA2", "ruleA2", "nameA2", categA, null);
+ ruleA2.setId(2);
+
+ ruleB1 = new Rule("ruleB1", "ruleB1", "nameB1", categB, null);
+ ruleB1.setId(3);
+
+ decorator = new ViolationsDecorator();
+ resource = mock(Resource.class);
+ context = mock(DecoratorContext.class);
+ when(context.getResource()).thenReturn(resource);
+ }
+
+ @Test
+ public void countViolations() throws Exception {
+ when(resource.getScope()).thenReturn(Resource.SCOPE_SET);
+ when(context.getViolations()).thenReturn(createViolations());
+ when(context.getChildrenMeasures((MeasuresFilter) anyObject())).thenReturn(Collections.<Measure>emptyList());
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.VIOLATIONS, 4.0)));
+ }
+
+ @Test
+ public void resetCountersAfterExecution() {
+ when(resource.getScope()).thenReturn(Resource.SCOPE_SET);
+ when(context.getViolations()).thenReturn(createViolations());
+ when(context.getChildrenMeasures((MeasuresFilter) anyObject())).thenReturn(Collections.<Measure>emptyList());
+
+ decorator.decorate(resource, context);
+ decorator.decorate(resource, context);
+
+ // we must not have 8 violations !
+ verify(context, times(2)).saveMeasure(argThat(new IsMeasure(CoreMetrics.VIOLATIONS, 4.0)));
+ verify(context, never()).saveMeasure(argThat(new IsMeasure(CoreMetrics.VIOLATIONS, 8.0)));
+ }
+
+ @Test
+ public void saveZeroOnProjects() throws Exception {
+ when(resource.getScope()).thenReturn(Resource.SCOPE_SET);
+ when(context.getViolations()).thenReturn(Collections.<Violation>emptyList());
+ when(context.getChildrenMeasures((MeasuresFilter) anyObject())).thenReturn(Collections.<Measure>emptyList());
+
+ decorator.decorate(resource, context);
+
+ verify(context, atLeast(1)).saveMeasure(argThat(new IsMeasure(CoreMetrics.VIOLATIONS, 0.0)));
+ }
+
+ @Test
+ public void saveZeroOnDirectories() throws Exception {
+ when(resource.getScope()).thenReturn(Resource.SCOPE_SPACE);
+ when(context.getViolations()).thenReturn(Collections.<Violation>emptyList());
+ when(context.getChildrenMeasures((MeasuresFilter) anyObject())).thenReturn(Collections.<Measure>emptyList());
+
+ decorator.decorate(resource, context);
+
+ verify(context, atLeast(1)).saveMeasure(argThat(new IsMeasure(CoreMetrics.VIOLATIONS, 0.0)));
+ }
+
+ @Test
+ public void priorityViolations() throws Exception {
+ when(resource.getScope()).thenReturn(Resource.SCOPE_SET);
+ when(context.getViolations()).thenReturn(createViolations());
+ when(context.getChildrenMeasures((MeasuresFilter) anyObject())).thenReturn(Collections.<Measure>emptyList());
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.VIOLATIONS, null, null, RulePriority.BLOCKER, 0.0)));
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.VIOLATIONS, null, null, RulePriority.CRITICAL, 2.0)));
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.VIOLATIONS, null, null, RulePriority.MAJOR, 1.0)));
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.VIOLATIONS, null, null, RulePriority.MINOR, 1.0)));
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.VIOLATIONS, null, null, RulePriority.INFO, 0.0)));
+
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.BLOCKER_VIOLATIONS, 0.0)));
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.CRITICAL_VIOLATIONS, 2.0)));
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.MAJOR_VIOLATIONS, 1.0)));
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.MINOR_VIOLATIONS, 1.0)));
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.INFO_VIOLATIONS, 0.0)));
+ }
+
+ @Test
+ public void categoryViolations() throws Exception {
+ when(resource.getScope()).thenReturn(Resource.SCOPE_SET);
+ when(context.getViolations()).thenReturn(createViolations());
+ when(context.getChildrenMeasures((MeasuresFilter) anyObject())).thenReturn(Collections.<Measure>emptyList());
+
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.VIOLATIONS, null, categA.getId(), null, 3.0)));
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(CoreMetrics.VIOLATIONS, null, categB.getId(), null, 1.0)));
+ }
+
+
+ private List<Violation> createViolations() {
+ List<Violation> violations = new ArrayList<Violation>();
+ violations.add(new Violation(ruleA1).setPriority(RulePriority.CRITICAL));
+ violations.add(new Violation(ruleA1).setPriority(RulePriority.CRITICAL));
+ violations.add(new Violation(ruleA2).setPriority(RulePriority.MAJOR));
+ violations.add(new Violation(ruleB1).setPriority(RulePriority.MINOR));
+ return violations;
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ViolationsDensityDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ViolationsDensityDecoratorTest.java
new file mode 100644
index 00000000000..cf4bca69fc3
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/ViolationsDensityDecoratorTest.java
@@ -0,0 +1,173 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.sensors;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import org.junit.Test;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.*;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.*;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.Rule;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+public class ViolationsDensityDecoratorTest {
+
+ @Test
+ public void calculateDensity() {
+ assertThat(ViolationsDensityDecorator.calculate(4000, 200), is(0.0));
+ assertThat(ViolationsDensityDecorator.calculate(200, 200), is(0.0));
+ assertThat(ViolationsDensityDecorator.calculate(50, 200), is(75.0));
+ assertThat(ViolationsDensityDecorator.calculate(0, 200), is(100.0));
+ }
+
+
+ @Test
+ public void decorateDensity() {
+ Resource resource = mock(Resource.class);
+ when(resource.getScope()).thenReturn(Resource.SCOPE_SET);
+
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 200.0));
+ when(context.getMeasure(CoreMetrics.WEIGHTED_VIOLATIONS)).thenReturn(new Measure(CoreMetrics.WEIGHTED_VIOLATIONS, 50.0));
+
+ ViolationsDensityDecorator decorator = new ViolationsDensityDecorator(new HashMap());
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.VIOLATIONS_DENSITY, 75.0);
+ }
+
+ @Test
+ public void noDensityIfNoNcloc() {
+ Resource resource = mock(Resource.class);
+ when(resource.getScope()).thenReturn(Resource.SCOPE_SET);
+
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 0.0));
+ when(context.getMeasure(CoreMetrics.WEIGHTED_VIOLATIONS)).thenReturn(new Measure(CoreMetrics.WEIGHTED_VIOLATIONS, 50.0));
+
+ ViolationsDensityDecorator decorator = new ViolationsDensityDecorator(new HashMap());
+ decorator.decorate(resource, context);
+
+ verify(context, never()).saveMeasure(eq(CoreMetrics.VIOLATIONS_DENSITY), anyDouble());
+ }
+
+ @Test
+ public void saveDensityIfValueIsZero() {
+ Resource resource = mock(Resource.class);
+ when(resource.getScope()).thenReturn(Resource.SCOPE_SET);
+
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 200.0));
+ when(context.getMeasure(CoreMetrics.WEIGHTED_VIOLATIONS)).thenReturn(new Measure(CoreMetrics.WEIGHTED_VIOLATIONS, 5000.0));
+
+ ViolationsDensityDecorator decorator = new ViolationsDensityDecorator(new HashMap());
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.VIOLATIONS_DENSITY, 0.0);
+ }
+
+ @Test
+ public void densityIsHundredWhenNoDebt() {
+ Resource resource = mock(Resource.class);
+ when(resource.getScope()).thenReturn(Resource.SCOPE_SET);
+
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 200.0));
+
+ ViolationsDensityDecorator decorator = new ViolationsDensityDecorator(new HashMap());
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.VIOLATIONS_DENSITY, 100.0);
+ }
+
+ @Test
+ public void densityIsHundredWhenDebtIsZero() {
+ Resource resource = mock(Resource.class);
+ when(resource.getScope()).thenReturn(Resource.SCOPE_SET);
+
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 200.0));
+ when(context.getMeasure(CoreMetrics.WEIGHTED_VIOLATIONS)).thenReturn(new Measure(CoreMetrics.WEIGHTED_VIOLATIONS, 0.0));
+
+ ViolationsDensityDecorator decorator = new ViolationsDensityDecorator(new HashMap());
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.VIOLATIONS_DENSITY, 100.0);
+ }
+
+ @Test
+ public void densityByCategory() {
+ Map<Integer, Metric> metricByCategoryId = new HashMap<Integer, Metric>();
+ metricByCategoryId.put(3, CoreMetrics.USABILITY);
+ metricByCategoryId.put(5, CoreMetrics.EFFICIENCY);
+ metricByCategoryId.put(6, CoreMetrics.RELIABILITY);
+
+ Resource resource = mock(Resource.class);
+ when(resource.getScope()).thenReturn(Resource.SCOPE_SET);
+
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 200.0));
+ when(context.getMeasures((MeasuresFilter) anyObject()))
+ .thenReturn(Arrays.asList(
+ new RuleMeasure(CoreMetrics.WEIGHTED_VIOLATIONS, new Rule(), null, 3).setValue(50.0),
+ new RuleMeasure(CoreMetrics.WEIGHTED_VIOLATIONS, new Rule(), null, 5).setValue(0.0)));
+
+ ViolationsDensityDecorator decorator = new ViolationsDensityDecorator(metricByCategoryId);
+ decorator.decorate(resource, context);
+
+ verify(context).saveMeasure(CoreMetrics.USABILITY, 75.0);
+ verify(context).saveMeasure(CoreMetrics.EFFICIENCY, 100.0);
+ verify(context).saveMeasure(CoreMetrics.RELIABILITY, 100.0);
+ }
+
+ @Test
+ public void doNotCalculateDensityByCategoryOnEntities() {
+ Map<Integer, Metric> metricByCategoryId = new HashMap<Integer, Metric>();
+ metricByCategoryId.put(3, CoreMetrics.USABILITY);
+ metricByCategoryId.put(5, CoreMetrics.EFFICIENCY);
+ metricByCategoryId.put(6, CoreMetrics.RELIABILITY);
+
+ Resource resource = mock(Resource.class);
+ when(resource.getScope()).thenReturn(Resource.SCOPE_ENTITY);
+
+ DecoratorContext context = mock(DecoratorContext.class);
+ when(context.getMeasure(CoreMetrics.NCLOC)).thenReturn(new Measure(CoreMetrics.NCLOC, 200.0));
+ when(context.getMeasure(CoreMetrics.WEIGHTED_VIOLATIONS)).thenReturn(new Measure(CoreMetrics.WEIGHTED_VIOLATIONS, 50.0));
+ when(context.getMeasures((MeasuresFilter) anyObject()))
+ .thenReturn(Arrays.asList(
+ new RuleMeasure(CoreMetrics.WEIGHTED_VIOLATIONS, new Rule(), null, 3).setValue(50.0),
+ new RuleMeasure(CoreMetrics.WEIGHTED_VIOLATIONS, new Rule(), null, 5).setValue(0.0)));
+
+ ViolationsDensityDecorator decorator = new ViolationsDensityDecorator(metricByCategoryId);
+ decorator.decorate(resource, context);
+
+ verify(context, never()).saveMeasure(eq(CoreMetrics.USABILITY), anyDouble());
+ verify(context, never()).saveMeasure(eq(CoreMetrics.EFFICIENCY), anyDouble());
+ verify(context, never()).saveMeasure(eq(CoreMetrics.RELIABILITY), anyDouble());
+ verify(context, never()).saveMeasure(eq(CoreMetrics.MAINTAINABILITY), anyDouble());
+ verify(context).saveMeasure(CoreMetrics.VIOLATIONS_DENSITY, 75.0);
+ }
+}
diff --git a/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/WeightedViolationsDecoratorTest.java b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/WeightedViolationsDecoratorTest.java
new file mode 100644
index 00000000000..f02934a6dbf
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/sensors/WeightedViolationsDecoratorTest.java
@@ -0,0 +1,116 @@
+/*
+ * Sonar, open source software quality management tool.
+ * Copyright (C) 2009 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * Sonar is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * Sonar is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with Sonar; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
+ */
+package org.sonar.plugins.core.sensors;
+
+import org.junit.Test;
+import static org.mockito.Mockito.*;
+import org.sonar.api.batch.DecoratorContext;
+import org.sonar.api.measures.CoreMetrics;
+import org.sonar.api.measures.Measure;
+import org.sonar.api.measures.MeasuresFilter;
+import org.sonar.api.measures.RuleMeasure;
+import org.sonar.api.resources.Resource;
+import org.sonar.api.rules.Rule;
+import org.sonar.api.rules.RulePriority;
+import org.sonar.api.test.IsMeasure;
+import org.sonar.api.test.IsRuleMeasure;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+public class WeightedViolationsDecoratorTest {
+
+ private List<RuleMeasure> createViolationsMeasures() {
+ return Arrays.asList(
+ // categ 3
+ new RuleMeasure(CoreMetrics.VIOLATIONS, new Rule(), RulePriority.INFO, 3).setValue(40.0),
+ new RuleMeasure(CoreMetrics.VIOLATIONS, new Rule(), RulePriority.CRITICAL, 3).setValue(80.0),
+ new RuleMeasure(CoreMetrics.VIOLATIONS, new Rule(), RulePriority.BLOCKER, 3).setValue(90.0),
+
+ // categ 4
+ new RuleMeasure(CoreMetrics.VIOLATIONS, new Rule(), RulePriority.INFO, 4).setValue(10.0),
+ new RuleMeasure(CoreMetrics.VIOLATIONS, new Rule(), RulePriority.BLOCKER, 4).setValue(10.0)
+ );
+ }
+
+ private Map<RulePriority, Integer> createWeights() {
+ Map<RulePriority, Integer> weights = new HashMap();
+ weights.put(RulePriority.BLOCKER, 10);
+ weights.put(RulePriority.CRITICAL, 5);
+ weights.put(RulePriority.MAJOR, 2);
+ weights.put(RulePriority.MINOR, 1);
+ weights.put(RulePriority.INFO, 0);
+ return weights;
+ }
+
+ @Test
+ public void weightedViolations() {
+ Map<RulePriority, Integer> weights = createWeights();
+
+ WeightedViolationsDecorator decorator = new WeightedViolationsDecorator(weights);
+ DecoratorContext context = mock(DecoratorContext.class);
+
+ when(context.getMeasures((MeasuresFilter) anyObject())).thenReturn(createViolationsMeasures());
+
+ decorator.decorate(mock(Resource.class), context);
+
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.WEIGHTED_VIOLATIONS, (double) (100 * 10 + 80 * 5 + 50 * 0))));
+ verify(context).saveMeasure(argThat(new IsMeasure(CoreMetrics.WEIGHTED_VIOLATIONS, "INFO=50;CRITICAL=80;BLOCKER=100")));
+ }
+
+ @Test
+ public void doNotSaveZero() {
+ Map<RulePriority, Integer> weights = createWeights();
+
+ WeightedViolationsDecorator decorator = new WeightedViolationsDecorator(weights);
+ DecoratorContext context = mock(DecoratorContext.class);
+
+ when(context.getMeasures((MeasuresFilter) anyObject())).thenReturn(Arrays.asList());
+
+ decorator.decorate(mock(Resource.class), context);
+
+ verify(context, never()).saveMeasure((Measure) anyObject());
+ }
+
+ @Test
+ public void weightedViolationsOnCategories() {
+ Map<RulePriority, Integer> weights = createWeights();
+
+ WeightedViolationsDecorator decorator = new WeightedViolationsDecorator(weights);
+ DecoratorContext context = mock(DecoratorContext.class);
+
+ when(context.getMeasures((MeasuresFilter) anyObject())).thenReturn(createViolationsMeasures());
+
+ decorator.decorate(mock(Resource.class), context);
+
+ // categ 3
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(
+ CoreMetrics.WEIGHTED_VIOLATIONS, null, 3, null, 40.0 * 0 + 80.0 * 5 + 90.0 * 10)));
+
+ // categ 4
+ verify(context).saveMeasure(argThat(new IsRuleMeasure(
+ CoreMetrics.WEIGHTED_VIOLATIONS, null, 4, null, 10.0 * 0 + 10.0 * 10)));
+
+ }
+
+}
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeletedResourcesTest/purgeDeletedResources-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeletedResourcesTest/purgeDeletedResources-result.xml
new file mode 100644
index 00000000000..acb67c7fca2
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeletedResourcesTest/purgeDeletedResources-result.xml
@@ -0,0 +1,68 @@
+<dataset>
+
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="2" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+ <!--<snapshots depth="[null]" id="3" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="3"-->
+ <!--parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"-->
+ <!--path="[null]"/>-->
+
+ <snapshots depth="[null]" id="4" scope="FIL" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="4"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+
+ <!--<SNAPSHOT_SOURCES ID="1" SNAPSHOT_ID="3" DATA="foo"/>-->
+ <SNAPSHOT_SOURCES ID="2" SNAPSHOT_ID="4" DATA="foo"/>
+
+
+ <RULE_FAILURES ID="1" SNAPSHOT_ID="1" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg1" LINE="[null]"/>
+ <RULE_FAILURES ID="2" SNAPSHOT_ID="2" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg2" LINE="[null]"/>
+ <!--<RULE_FAILURES ID="3" SNAPSHOT_ID="3" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg3" LINE="[null]"/>-->
+ <RULE_FAILURES ID="4" SNAPSHOT_ID="4" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg4" LINE="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+ <measure_data id="1" measure_id="1" snapshot_id="1" data="[null]"/>
+ <measure_data id="2" measure_id="2" snapshot_id="2" data="[null]"/>
+ <!--<measure_data id="3" measure_id="3" snapshot_id="3" data="[null]"/>-->
+ <measure_data id="4" measure_id="4" snapshot_id="4" data="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeletedResourcesTest/purgeDeletedResources.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeletedResourcesTest/purgeDeletedResources.xml
new file mode 100644
index 00000000000..828e5275d7d
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeletedResourcesTest/purgeDeletedResources.xml
@@ -0,0 +1,68 @@
+<dataset>
+
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="2" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="3" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="4" scope="FIL" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="4"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+
+ <SNAPSHOT_SOURCES ID="1" SNAPSHOT_ID="3" DATA="foo"/>
+ <SNAPSHOT_SOURCES ID="2" SNAPSHOT_ID="4" DATA="foo"/>
+
+
+ <RULE_FAILURES ID="1" SNAPSHOT_ID="1" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg1" LINE="[null]"/>
+ <RULE_FAILURES ID="2" SNAPSHOT_ID="2" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg2" LINE="[null]"/>
+ <RULE_FAILURES ID="3" SNAPSHOT_ID="3" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg3" LINE="[null]"/>
+ <RULE_FAILURES ID="4" SNAPSHOT_ID="4" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg4" LINE="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+ <measure_data id="1" measure_id="1" snapshot_id="1" data="[null]"/>
+ <measure_data id="2" measure_id="2" snapshot_id="2" data="[null]"/>
+ <measure_data id="3" measure_id="3" snapshot_id="3" data="[null]"/>
+ <measure_data id="4" measure_id="4" snapshot_id="4" data="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeletedResourcesTest/sharedFixture.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeletedResourcesTest/sharedFixture.xml
new file mode 100644
index 00000000000..8d4d27778c7
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeletedResourcesTest/sharedFixture.xml
@@ -0,0 +1,33 @@
+<dataset>
+ <rules_categories id="1" name="category one" description="[null]"/>
+ <rules id="1" name="foo" rules_category_id="1" plugin_config_key="checker/foo" plugin_rule_key="checkstyle.rule1"
+ plugin_name="maven-checkstyle-plugin" description="description" cardinality="SINGLE" parent_id="[null]"/>
+
+ <metrics id="1" name="ncloc" val_type="INT" description="[null]" domain="[null]"
+ short_name="" qualitative="false" user_managed="false" enabled="true" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/>
+
+ <!-- project -->
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- package -->
+ <projects long_name="[null]" id="2" scope="DIR" qualifier="PAC" kee="mygroup:myartifact:my.package" name="package"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- file -->
+ <projects long_name="[null]" id="3" scope="FIL" qualifier="CLA" kee="mygroup:myartifact:my.package.Class1"
+ name="class" root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- project copy for views -->
+ <projects long_name="[null]" id="4" scope="FIL" qualifier="TRK" kee="cp-mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeprecatedLastTest/purgeDeprecatedLast-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeprecatedLastTest/purgeDeprecatedLast-result.xml
new file mode 100644
index 00000000000..daebf0a394a
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeprecatedLastTest/purgeDeprecatedLast-result.xml
@@ -0,0 +1,71 @@
+<dataset>
+
+ <!-- old snapshots -->
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="2" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+
+ <!-- last but project is not last -->
+ <!--<snapshots depth="[null]" id="3" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="3"-->
+ <!--parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="true"-->
+ <!--path="[null]"/>-->
+
+ <snapshots depth="[null]" id="4" scope="FIL" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="4"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+
+ <!--<SNAPSHOT_SOURCES ID="1" SNAPSHOT_ID="3" DATA="foo"/>-->
+ <SNAPSHOT_SOURCES ID="2" SNAPSHOT_ID="4" DATA="foo"/>
+
+ <RULE_FAILURES ID="1" SNAPSHOT_ID="1" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg1" LINE="[null]"/>
+ <RULE_FAILURES ID="2" SNAPSHOT_ID="2" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg2" LINE="[null]"/>
+ <!--<RULE_FAILURES ID="3" SNAPSHOT_ID="3" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg3" LINE="[null]"/>-->
+ <RULE_FAILURES ID="4" SNAPSHOT_ID="4" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg4" LINE="[null]"/>
+
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+ <measure_data id="1" measure_id="1" snapshot_id="1" data="[null]"/>
+ <measure_data id="2" measure_id="2" snapshot_id="2" data="[null]"/>
+ <!--<measure_data id="3" measure_id="3" snapshot_id="3" data="[null]"/>-->
+ <measure_data id="4" measure_id="4" snapshot_id="4" data="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeprecatedLastTest/purgeDeprecatedLast.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeprecatedLastTest/purgeDeprecatedLast.xml
new file mode 100644
index 00000000000..29b9f3ada99
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeprecatedLastTest/purgeDeprecatedLast.xml
@@ -0,0 +1,71 @@
+<dataset>
+
+ <!-- old snapshots -->
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="2" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+
+ <!-- last but project is not last -->
+ <snapshots depth="[null]" id="3" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="4" scope="FIL" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="4"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+
+ <SNAPSHOT_SOURCES ID="1" SNAPSHOT_ID="3" DATA="foo"/>
+ <SNAPSHOT_SOURCES ID="2" SNAPSHOT_ID="4" DATA="foo"/>
+
+ <RULE_FAILURES ID="1" SNAPSHOT_ID="1" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg1" LINE="[null]"/>
+ <RULE_FAILURES ID="2" SNAPSHOT_ID="2" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg2" LINE="[null]"/>
+ <RULE_FAILURES ID="3" SNAPSHOT_ID="3" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg3" LINE="[null]"/>
+ <RULE_FAILURES ID="4" SNAPSHOT_ID="4" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg4" LINE="[null]"/>
+
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+ <measure_data id="1" measure_id="1" snapshot_id="1" data="[null]"/>
+ <measure_data id="2" measure_id="2" snapshot_id="2" data="[null]"/>
+ <measure_data id="3" measure_id="3" snapshot_id="3" data="[null]"/>
+ <measure_data id="4" measure_id="4" snapshot_id="4" data="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeprecatedLastTest/sharedFixture.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeprecatedLastTest/sharedFixture.xml
new file mode 100644
index 00000000000..8d4d27778c7
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDeprecatedLastTest/sharedFixture.xml
@@ -0,0 +1,33 @@
+<dataset>
+ <rules_categories id="1" name="category one" description="[null]"/>
+ <rules id="1" name="foo" rules_category_id="1" plugin_config_key="checker/foo" plugin_rule_key="checkstyle.rule1"
+ plugin_name="maven-checkstyle-plugin" description="description" cardinality="SINGLE" parent_id="[null]"/>
+
+ <metrics id="1" name="ncloc" val_type="INT" description="[null]" domain="[null]"
+ short_name="" qualitative="false" user_managed="false" enabled="true" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/>
+
+ <!-- project -->
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- package -->
+ <projects long_name="[null]" id="2" scope="DIR" qualifier="PAC" kee="mygroup:myartifact:my.package" name="package"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- file -->
+ <projects long_name="[null]" id="3" scope="FIL" qualifier="CLA" kee="mygroup:myartifact:my.package.Class1"
+ name="class" root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- project copy for views -->
+ <projects long_name="[null]" id="4" scope="FIL" qualifier="TRK" kee="cp-mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/nothingToPurge-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/nothingToPurge-result.xml
new file mode 100644
index 00000000000..c7125729940
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/nothingToPurge-result.xml
@@ -0,0 +1,89 @@
+<dataset>
+
+ <!-- project -->
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- module -->
+ <projects long_name="[null]" id="2" scope="PRJ" qualifier="BRC" kee="mygroup:mymoduleartifact" name="module"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- package -->
+ <projects long_name="[null]" id="3" scope="DIR" qualifier="PAC" kee="mygroup:myartifact:my.package" name="package"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- class -->
+ <projects long_name="[null]" id="4" scope="FIL" qualifier="CLA" kee="mygroup:myartifact:my.package.Class1"
+ name="Class1" root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="1"/>
+
+ <snapshots depth="0" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path=""/>
+
+ <snapshots depth="1" id="2" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="1" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="1."/>
+
+ <snapshots depth="2" id="3" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="1.2."/>
+
+ <snapshots depth="3" id="4" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="4"
+ parent_snapshot_id="3" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="1.2.3."/>
+
+ <snapshots depth="0" id="5" scope="PRJ" qualifier="TRK" created_at="2009-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path=""/>
+
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="5" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="5" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/nothingToPurge.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/nothingToPurge.xml
new file mode 100644
index 00000000000..c7125729940
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/nothingToPurge.xml
@@ -0,0 +1,89 @@
+<dataset>
+
+ <!-- project -->
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- module -->
+ <projects long_name="[null]" id="2" scope="PRJ" qualifier="BRC" kee="mygroup:mymoduleartifact" name="module"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- package -->
+ <projects long_name="[null]" id="3" scope="DIR" qualifier="PAC" kee="mygroup:myartifact:my.package" name="package"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- class -->
+ <projects long_name="[null]" id="4" scope="FIL" qualifier="CLA" kee="mygroup:myartifact:my.package.Class1"
+ name="Class1" root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="1"/>
+
+ <snapshots depth="0" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path=""/>
+
+ <snapshots depth="1" id="2" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="1" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="1."/>
+
+ <snapshots depth="2" id="3" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="1.2."/>
+
+ <snapshots depth="3" id="4" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="4"
+ parent_snapshot_id="3" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="1.2.3."/>
+
+ <snapshots depth="0" id="5" scope="PRJ" qualifier="TRK" created_at="2009-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path=""/>
+
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="5" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="5" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/purgeDisabledModule-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/purgeDisabledModule-result.xml
new file mode 100644
index 00000000000..fbe36205e74
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/purgeDisabledModule-result.xml
@@ -0,0 +1,75 @@
+<dataset>
+
+ <!-- project -->
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- disabled module -->
+ <!--<projects long_name="[null]" id="2" scope="PRJ" qualifier="BRC" kee="mygroup:mymoduleartifact" name="module" root_id="[null]"-->
+ <!--description="[null]"-->
+ <!--enabled="false" language="java" copy_resource_id="[null]"/>-->
+
+ <!-- package -->
+ <!--<projects long_name="[null]" id="3" scope="DIR" qualifier="PAC" kee="mygroup:myartifact:my.package" name="package" root_id="[null]"-->
+ <!--description="[null]"-->
+ <!--enabled="true" language="java" copy_resource_id="[null]"/>-->
+
+ <!-- class -->
+ <!--<projects long_name="[null]" id="4" scope="FIL" qualifier="CLA" kee="mygroup:myartifact:my.package.Class1" name="Class1" root_id="[null]"-->
+ <!--description="[null]"-->
+ <!--enabled="true" language="java" copy_resource_id="1"/>-->
+
+
+ <snapshots depth="0" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path=""/>
+
+ <!--<snapshots depth="1" id="2" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="2"-->
+ <!--parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"-->
+ <!--path="1."/>-->
+
+ <!--<snapshots depth="2" id="3" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="3"-->
+ <!--parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"-->
+ <!--path="1.2."/>-->
+
+ <!--<snapshots depth="3" id="4" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="4"-->
+ <!--parent_snapshot_id="3" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"-->
+ <!--path="1.2.3."/>-->
+
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/purgeDisabledModule.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/purgeDisabledModule.xml
new file mode 100644
index 00000000000..90abf33096c
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/purgeDisabledModule.xml
@@ -0,0 +1,78 @@
+<dataset>
+
+ <!-- project -->
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- disabled module -->
+ <projects long_name="[null]" id="2" scope="PRJ" qualifier="BRC" kee="mygroup:mymoduleartifact" name="module"
+ root_id="[null]"
+ description="[null]"
+ enabled="false" language="java" copy_resource_id="[null]"/>
+
+ <!-- package -->
+ <projects long_name="[null]" id="3" scope="DIR" qualifier="PAC" kee="mygroup:myartifact:my.package" name="package"
+ root_id="[null]"
+ description="[null]"
+ enabled="false" language="java" copy_resource_id="[null]"/>
+
+ <!-- class -->
+ <projects long_name="[null]" id="4" scope="FIL" qualifier="CLA" kee="mygroup:myartifact:my.package.Class1"
+ name="Class1" root_id="[null]"
+ description="[null]"
+ enabled="false" language="java" copy_resource_id="1"/>
+
+
+ <snapshots depth="0" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path=""/>
+
+ <snapshots depth="1" id="2" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="1" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="1."/>
+
+ <snapshots depth="2" id="3" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="1.2."/>
+
+ <snapshots depth="3" id="4" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="4"
+ parent_snapshot_id="3" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="1.2.3."/>
+
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/purgeDisabledProject-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/purgeDisabledProject-result.xml
new file mode 100644
index 00000000000..e99e5ca29af
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/purgeDisabledProject-result.xml
@@ -0,0 +1,106 @@
+<dataset>
+
+ <!-- disabled project -->
+ <!--<projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project" root_id="[null]"-->
+ <!--description="[null]"-->
+ <!--enabled="false" language="java" copy_resource_id="[null]"/>-->
+
+ <!-- disabled module -->
+ <!--<projects long_name="[null]" id="2" scope="PRJ" qualifier="BRC" kee="mygroup:mymoduleartifact" name="module" root_id="[null]"-->
+ <!--description="[null]"-->
+ <!--enabled="false" language="java" copy_resource_id="[null]"/>-->
+
+ <!-- package -->
+ <!--<projects long_name="[null]" id="3" scope="DIR" qualifier="PAC" kee="mygroup:myartifact:my.package" name="package" root_id="[null]"-->
+ <!--description="[null]"-->
+ <!--enabled="true" language="java" copy_resource_id="[null]"/>-->
+
+ <!-- class -->
+ <!--<projects long_name="[null]" id="4" scope="FIL" qualifier="CLA" kee="mygroup:myartifact:my.package.Class1" name="Class1" root_id="[null]"-->
+ <!--description="[null]"-->
+ <!--enabled="true" language="java" copy_resource_id="1"/>-->
+
+
+ <!-- the same enabled project -->
+ <projects long_name="[null]" id="5" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+
+ <!--<snapshots depth="0" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="1"-->
+ <!--parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"-->
+ <!--path=""/>-->
+
+ <!--<snapshots depth="1" id="2" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="2"-->
+ <!--parent_snapshot_id="1" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"-->
+ <!--path="1."/>-->
+
+ <!--<snapshots depth="2" id="3" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="3"-->
+ <!--parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"-->
+ <!--path="1.2."/>-->
+
+ <!--<snapshots depth="3" id="4" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="4"-->
+ <!--parent_snapshot_id="3" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"-->
+ <!--path="1.2.3."/>-->
+
+ <!--<snapshots depth="0" id="5" scope="PRJ" qualifier="TRK" created_at="2009-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="1"-->
+ <!--parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"-->
+ <!--path=""/>-->
+
+
+ <!-- enabled -->
+ <snapshots depth="0" id="6" scope="PRJ" qualifier="TRK" created_at="2009-02-14 13:58:00.00" version="[null]"
+ project_id="5"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path=""/>
+
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="5" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="5" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="6" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="6" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/purgeDisabledProject.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/purgeDisabledProject.xml
new file mode 100644
index 00000000000..884a048a5fb
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/purgeDisabledProject.xml
@@ -0,0 +1,112 @@
+<dataset>
+
+ <!-- disabled project -->
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="false" language="java" copy_resource_id="[null]"/>
+
+ <!-- disabled module -->
+ <projects long_name="[null]" id="2" scope="PRJ" qualifier="BRC" kee="mygroup:mymoduleartifact" name="module"
+ root_id="[null]"
+ description="[null]"
+ enabled="false" language="java" copy_resource_id="[null]"/>
+
+ <!-- package -->
+ <projects long_name="[null]" id="3" scope="DIR" qualifier="PAC" kee="mygroup:myartifact:my.package" name="package"
+ root_id="[null]"
+ description="[null]"
+ enabled="false" language="java" copy_resource_id="[null]"/>
+
+ <!-- class -->
+ <projects long_name="[null]" id="4" scope="FIL" qualifier="CLA" kee="mygroup:myartifact:my.package.Class1"
+ name="Class1" root_id="[null]"
+ description="[null]"
+ enabled="false" language="java" copy_resource_id="1"/>
+
+
+ <!-- the same enabled project -->
+ <projects long_name="[null]" id="5" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+
+ <!-- disabled -->
+ <snapshots depth="0" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path=""/>
+
+ <snapshots depth="1" id="2" scope="PRJ" qualifier="BRC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="1" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="1."/>
+
+ <snapshots depth="2" id="3" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="1.2."/>
+
+ <snapshots depth="3" id="4" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="4"
+ parent_snapshot_id="3" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="1.2.3."/>
+
+ <snapshots depth="0" id="5" scope="PRJ" qualifier="TRK" created_at="2009-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path=""/>
+
+
+ <!-- enabled -->
+ <snapshots depth="0" id="6" scope="PRJ" qualifier="TRK" created_at="2009-02-14 13:58:00.00" version="[null]"
+ project_id="5"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path=""/>
+
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="5" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="5" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="6" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="6" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/sharedFixture.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/sharedFixture.xml
new file mode 100644
index 00000000000..eee1bfaad3c
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeDisabledResourcesTest/sharedFixture.xml
@@ -0,0 +1,9 @@
+<dataset>
+ <rules_categories id="1" name="category one" description="[null]"/>
+ <rules id="1" name="foo" rules_category_id="1" plugin_config_key="checker/foo" plugin_rule_key="checkstyle.rule1"
+ plugin_name="maven-checkstyle-plugin" description="description" cardinality="SINGLE" parent_id="[null]"/>
+
+ <metrics id="1" name="ncloc" val_type="INT" description="[null]" domain="[null]"
+ short_name="" qualitative="false" user_managed="false" enabled="true" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEntitiesTest/purgeEntities-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEntitiesTest/purgeEntities-result.xml
new file mode 100644
index 00000000000..3bdf1da2894
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEntitiesTest/purgeEntities-result.xml
@@ -0,0 +1,141 @@
+<dataset>
+
+ <!-- old snapshots -->
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="2" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+ <!--<snapshots depth="[null]" id="3" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="3"-->
+ <!--parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"-->
+ <!--path="[null]"/>-->
+
+ <!--<snapshots depth="[null]" id="4" scope="FIL" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="4"-->
+ <!--parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"-->
+ <!--path="[null]"/>-->
+
+
+ <!-- last snapshots -->
+
+ <snapshots depth="[null]" id="5" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="6" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="5" root_project_id="[null]" root_snapshot_id="5" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="7" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="6" root_project_id="[null]" root_snapshot_id="5" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="8" scope="FIL" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="4"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+
+ <!-- old source -->
+ <!--<SNAPSHOT_SOURCES ID="1" SNAPSHOT_ID="3" DATA="foo"/>-->
+
+ <!-- last source -->
+ <SNAPSHOT_SOURCES ID="2" SNAPSHOT_ID="7" DATA="foo"/>
+
+
+ <!-- old violations -->
+ <RULE_FAILURES ID="1" SNAPSHOT_ID="1" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg1" LINE="[null]"/>
+ <RULE_FAILURES ID="2" SNAPSHOT_ID="2" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg2" LINE="[null]"/>
+ <!--<RULE_FAILURES ID="3" SNAPSHOT_ID="3" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg3"/>-->
+ <!--<RULE_FAILURES ID="4" SNAPSHOT_ID="4" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg4"/>-->
+
+
+ <!-- last violations -->
+ <RULE_FAILURES ID="5" SNAPSHOT_ID="5" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg1"/>
+ <RULE_FAILURES ID="6" SNAPSHOT_ID="6" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg2"/>
+ <RULE_FAILURES ID="7" SNAPSHOT_ID="7" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg3"/>
+ <RULE_FAILURES ID="8" SNAPSHOT_ID="8" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg4"/>
+
+ <!-- old measures -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+
+ <!-- last measures -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="5" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="5" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="6" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="6" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="7" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="7" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="8" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="8" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+ <!-- old measure data -->
+ <measure_data id="1" measure_id="1" snapshot_id="1" data="[null]"/>
+ <measure_data id="2" measure_id="2" snapshot_id="2" data="[null]"/>
+ <!--<measure_data id="3" measure_id="3" snapshot_id="3" data="[null]"/>-->
+ <!--<measure_data id="4" measure_id="4" snapshot_id="4" data="[null]"/>-->
+
+ <!-- last measure data -->
+ <measure_data id="5" measure_id="5" snapshot_id="5" data="[null]"/>
+ <measure_data id="6" measure_id="6" snapshot_id="6" data="[null]"/>
+ <measure_data id="7" measure_id="7" snapshot_id="7" data="[null]"/>
+ <measure_data id="8" measure_id="8" snapshot_id="8" data="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEntitiesTest/purgeEntities.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEntitiesTest/purgeEntities.xml
new file mode 100644
index 00000000000..a93593ff164
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEntitiesTest/purgeEntities.xml
@@ -0,0 +1,141 @@
+<dataset>
+
+ <!-- old snapshots -->
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="2" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="3" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="4" scope="FIL" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="4"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+
+ <!-- last snapshots -->
+
+ <snapshots depth="[null]" id="5" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="6" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="5" root_project_id="[null]" root_snapshot_id="5" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="7" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="6" root_project_id="[null]" root_snapshot_id="5" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="8" scope="FIL" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="4"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+
+ <!-- old source -->
+ <SNAPSHOT_SOURCES ID="1" SNAPSHOT_ID="3" DATA="foo"/>
+
+ <!-- last source -->
+ <SNAPSHOT_SOURCES ID="2" SNAPSHOT_ID="7" DATA="foo"/>
+
+
+ <!-- old violations -->
+ <RULE_FAILURES ID="1" SNAPSHOT_ID="1" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg1" LINE="[null]"/>
+ <RULE_FAILURES ID="2" SNAPSHOT_ID="2" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg2" LINE="[null]"/>
+ <RULE_FAILURES ID="3" SNAPSHOT_ID="3" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg3" LINE="[null]"/>
+ <RULE_FAILURES ID="4" SNAPSHOT_ID="4" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg4" LINE="[null]"/>
+
+
+ <!-- last violations -->
+ <RULE_FAILURES ID="5" SNAPSHOT_ID="5" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg1" LINE="[null]"/>
+ <RULE_FAILURES ID="6" SNAPSHOT_ID="6" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg2" LINE="[null]"/>
+ <RULE_FAILURES ID="7" SNAPSHOT_ID="7" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg3" LINE="[null]"/>
+ <RULE_FAILURES ID="8" SNAPSHOT_ID="8" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg4" LINE="[null]"/>
+
+ <!-- old measures -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+ <!-- last measures -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="5" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="5" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="6" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="6" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="7" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="7" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="8" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="8" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+ <!-- old measure data -->
+ <measure_data id="1" measure_id="1" snapshot_id="1" data="[null]"/>
+ <measure_data id="2" measure_id="2" snapshot_id="2" data="[null]"/>
+ <measure_data id="3" measure_id="3" snapshot_id="3" data="[null]"/>
+ <measure_data id="4" measure_id="4" snapshot_id="4" data="[null]"/>
+
+ <!-- last measure data -->
+ <measure_data id="5" measure_id="5" snapshot_id="5" data="[null]"/>
+ <measure_data id="6" measure_id="6" snapshot_id="6" data="[null]"/>
+ <measure_data id="7" measure_id="7" snapshot_id="7" data="[null]"/>
+ <measure_data id="8" measure_id="8" snapshot_id="8" data="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEntitiesTest/sharedFixture.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEntitiesTest/sharedFixture.xml
new file mode 100644
index 00000000000..8d4d27778c7
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEntitiesTest/sharedFixture.xml
@@ -0,0 +1,33 @@
+<dataset>
+ <rules_categories id="1" name="category one" description="[null]"/>
+ <rules id="1" name="foo" rules_category_id="1" plugin_config_key="checker/foo" plugin_rule_key="checkstyle.rule1"
+ plugin_name="maven-checkstyle-plugin" description="description" cardinality="SINGLE" parent_id="[null]"/>
+
+ <metrics id="1" name="ncloc" val_type="INT" description="[null]" domain="[null]"
+ short_name="" qualitative="false" user_managed="false" enabled="true" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/>
+
+ <!-- project -->
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- package -->
+ <projects long_name="[null]" id="2" scope="DIR" qualifier="PAC" kee="mygroup:myartifact:my.package" name="package"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- file -->
+ <projects long_name="[null]" id="3" scope="FIL" qualifier="CLA" kee="mygroup:myartifact:my.package.Class1"
+ name="class" root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- project copy for views -->
+ <projects long_name="[null]" id="4" scope="FIL" qualifier="TRK" kee="cp-mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEventOrphansTest/purgeEventOrphans-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEventOrphansTest/purgeEventOrphans-result.xml
new file mode 100644
index 00000000000..46bd9b39c93
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEventOrphansTest/purgeEventOrphans-result.xml
@@ -0,0 +1,16 @@
+<dataset>
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" profile_id="[null]"/>
+
+ <!-- global event -->
+ <events id="1" name="Upgrade" resource_id="[null]" snapshot_id="[null]" category="SYSTEM" description="[null]" event_date="2008-12-02 13:58:00.00" CREATED_AT="[null]" data="[null]" />
+
+ <!-- project event -->
+ <events id="2" name="Version 1.0" resource_id="1" snapshot_id="[null]" category="VERSION" description="[null]" event_date="2008-12-02 13:58:00.00" CREATED_AT="[null]" data="[null]"/>
+
+ <!-- orphan : the project does not exist-->
+ <!--<events id="3" name="Version 0.9" resource_id="5" snapshot_id="[null]" category="VERSION" description="[null]" event_date="2008-12-02 13:58:00.00" CREATED_AT="[null]" data="[null]"/> -->
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEventOrphansTest/purgeEventOrphans.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEventOrphansTest/purgeEventOrphans.xml
new file mode 100644
index 00000000000..6e462083c40
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeEventOrphansTest/purgeEventOrphans.xml
@@ -0,0 +1,16 @@
+<dataset>
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" profile_id="[null]"/>
+
+ <!-- global event -->
+ <events id="1" name="Upgrade" resource_id="[null]" snapshot_id="[null]" category="SYSTEM" description="[null]" event_date="2008-12-02 13:58:00.00" CREATED_AT="[null]" data="[null]"/>
+
+ <!-- project event -->
+ <events id="2" name="Version 1.0" resource_id="1" snapshot_id="[null]" category="VERSION" description="[null]" event_date="2008-12-02 13:58:00.00" CREATED_AT="[null]" data="[null]"/>
+
+ <!-- orphan : the project does not exist-->
+ <events id="3" name="Version 0.9" resource_id="5" snapshot_id="[null]" category="VERSION" description="[null]" event_date="2008-12-02 13:58:00.00" CREATED_AT="[null]" data="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeOrphanResourcesTest/purgeOrphanResources-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeOrphanResourcesTest/purgeOrphanResources-result.xml
new file mode 100644
index 00000000000..7b43ac967c3
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeOrphanResourcesTest/purgeOrphanResources-result.xml
@@ -0,0 +1,23 @@
+<dataset>
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="other-project" name="other-project"
+ root_id="[null]" profile_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- project1 has been removed from UI -->
+ <!--<projects long_name="[null]" id="2" scope="PRJ" qualifier="TRK" kee="project1" name="project1"-->
+ <!--root_id="[null]"-->
+ <!--description="[null]"-->
+ <!--enabled="true" language="java" copy_resource_id="[null]"/>-->
+
+ <!--<projects long_name="[null]" id="3" scope="PRJ" qualifier="BRC" kee="sub-project1" name="sub-project1"-->
+ <!--root_id="2"-->
+ <!--description="[null]"-->
+ <!--enabled="true" language="java" copy_resource_id="[null]"/>-->
+
+ <!--<projects long_name="[null]" id="4" scope="DIR" qualifier="PAC" kee="package" name="package of sup-project1"-->
+ <!--root_id="2"-->
+ <!--description="[null]"-->
+ <!--enabled="true" language="java" copy_resource_id="[null]"/>-->
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeOrphanResourcesTest/purgeOrphanResources.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeOrphanResourcesTest/purgeOrphanResources.xml
new file mode 100644
index 00000000000..0be6420e8d1
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeOrphanResourcesTest/purgeOrphanResources.xml
@@ -0,0 +1,23 @@
+<dataset>
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="other-project" name="other-project"
+ root_id="[null]" profile_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- project1 has been removed from UI -->
+ <!--<projects long_name="[null]" id="2" scope="PRJ" qualifier="TRK" kee="project1" name="project1"-->
+ <!--root_id="[null]"-->
+ <!--description="[null]"-->
+ <!--enabled="true" language="java" copy_resource_id="[null]"/>-->
+
+ <projects long_name="[null]" id="3" scope="PRJ" qualifier="BRC" kee="sub-project1" name="sub-project1"
+ root_id="2" profile_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <projects long_name="[null]" id="4" scope="DIR" qualifier="PAC" kee="package" name="package of sup-project1"
+ root_id="2" profile_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgePropertyOrphansTest/purgeResourceOrphans-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgePropertyOrphansTest/purgeResourceOrphans-result.xml
new file mode 100644
index 00000000000..e2cf33e7e40
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgePropertyOrphansTest/purgeResourceOrphans-result.xml
@@ -0,0 +1,13 @@
+<dataset>
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="other-project" name="other-project"
+ root_id="[null]" profile_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <properties id="1" prop_key="one" text_value="one" resource_id="[null]" user_id="[null]"/>
+ <properties id="2" prop_key="one" text_value="one" resource_id="1" user_id="[null]"/>
+
+ <!-- resource 2 does not exist anymore -->
+ <!--<properties id="3" prop_key="one" text_value="one" resource_id="2" user_id="[null]"/>-->
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgePropertyOrphansTest/purgeResourceOrphans.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgePropertyOrphansTest/purgeResourceOrphans.xml
new file mode 100644
index 00000000000..35b75197444
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgePropertyOrphansTest/purgeResourceOrphans.xml
@@ -0,0 +1,11 @@
+<dataset>
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="other-project" name="other-project"
+ root_id="[null]" profile_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <properties id="1" prop_key="one" text_value="one" resource_id="[null]" user_id="[null]"/>
+ <properties id="2" prop_key="one" text_value="one" resource_id="1" user_id="[null]"/>
+ <properties id="3" prop_key="one" text_value="one" resource_id="2" user_id="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgePropertyOrphansTest/purgeUserOrphans-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgePropertyOrphansTest/purgeUserOrphans-result.xml
new file mode 100644
index 00000000000..44ac13dd9d6
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgePropertyOrphansTest/purgeUserOrphans-result.xml
@@ -0,0 +1,10 @@
+<dataset>
+ <users id="1" login="me" name="Me" email="me@you.com" />
+
+ <properties id="1" prop_key="one" text_value="one" resource_id="[null]" user_id="[null]"/>
+ <properties id="2" prop_key="one" text_value="one" resource_id="[null]" user_id="1"/>
+
+ <!-- user 2 has been deleted -->
+ <!--<properties id="3" prop_key="one" text_value="one" resource_id="[null]" user_id="2"/>-->
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgePropertyOrphansTest/purgeUserOrphans.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgePropertyOrphansTest/purgeUserOrphans.xml
new file mode 100644
index 00000000000..1b595f06123
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgePropertyOrphansTest/purgeUserOrphans.xml
@@ -0,0 +1,8 @@
+<dataset>
+ <users id="1" login="me" name="Me" email="me@you.com" />
+
+ <properties id="1" prop_key="one" text_value="one" resource_id="[null]" user_id="[null]"/>
+ <properties id="2" prop_key="one" text_value="one" resource_id="[null]" user_id="1"/>
+ <properties id="3" prop_key="one" text_value="one" resource_id="[null]" user_id="2"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeResourceRolesTest/purgeResourceRoles-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeResourceRolesTest/purgeResourceRoles-result.xml
new file mode 100644
index 00000000000..c4c0be5ed7a
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeResourceRolesTest/purgeResourceRoles-result.xml
@@ -0,0 +1,27 @@
+<dataset>
+
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" profile_id="[null]" />
+
+ <!-- existing project -->
+ <user_roles id="1" user_id="30" resource_id="1" role="admin" />
+ <user_roles id="2" user_id="31" resource_id="1" role="user" />
+
+ <!-- deleted project -->
+ <!--<user_roles id="3" user_id="30" resource_id="90" role="admin" />-->
+ <!--<user_roles id="4" user_id="31" resource_id="90" role="user" />-->
+
+
+ <!-- existing project -->
+ <group_roles id="1" group_id="40" resource_id="1" role="admin" />
+ <group_roles id="2" group_id="44" resource_id="1" role="user" />
+
+ <!-- deleted project -->
+ <!--<group_roles id="3" group_id="60" resource_id="90" role="admin" />-->
+ <!--<group_roles id="4" group_id="61" resource_id="90" role="user" />-->
+
+ <!-- global roles -->
+ <group_roles id="5" group_id="60" resource_id="[null]" role="admin" />
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeResourceRolesTest/purgeResourceRoles.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeResourceRolesTest/purgeResourceRoles.xml
new file mode 100644
index 00000000000..0678e9ec63b
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeResourceRolesTest/purgeResourceRoles.xml
@@ -0,0 +1,27 @@
+<dataset>
+
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]" profile_id="[null]"/>
+
+ <!-- existing project -->
+ <user_roles id="1" user_id="30" resource_id="1" role="admin" />
+ <user_roles id="2" user_id="31" resource_id="1" role="user" />
+
+ <!-- deleted project -->
+ <user_roles id="3" user_id="30" resource_id="90" role="admin" />
+ <user_roles id="4" user_id="31" resource_id="90" role="user" />
+
+
+ <!-- existing project -->
+ <group_roles id="1" group_id="40" resource_id="1" role="admin" />
+ <group_roles id="2" group_id="44" resource_id="1" role="user" />
+
+ <!-- deleted project -->
+ <group_roles id="3" group_id="60" resource_id="90" role="admin" />
+ <group_roles id="4" group_id="61" resource_id="90" role="user" />
+
+ <!-- global roles -->
+ <group_roles id="5" group_id="60" resource_id="[null]" role="admin" />
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeRuleMeasuresTest/purgeRuleMeasures-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeRuleMeasuresTest/purgeRuleMeasures-result.xml
new file mode 100644
index 00000000000..86dbc05aba1
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeRuleMeasuresTest/purgeRuleMeasures-result.xml
@@ -0,0 +1,91 @@
+<dataset>
+
+ <!-- old snapshots -->
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="2" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="3" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+
+ <!-- last snapshots -->
+
+ <snapshots depth="[null]" id="4" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="5" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="4" root_project_id="[null]" root_snapshot_id="4" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="6" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="5" root_project_id="[null]" root_snapshot_id="4" status="P" islast="true"
+ path="[null]"/>
+
+ <!-- old measures -->
+ <project_measures characteristic_id="[null]" rule_priority="[null]" RULE_ID="[null]" RULES_CATEGORY_ID="[null]"
+ url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="4"-->
+ <!--alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="[null]"-->
+ <!--RULE_ID="[null]"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="[null]"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+
+ <!-- last measures -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="[null]"
+ RULE_ID="[null]"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="5" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="5" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="4"
+ alert_text="[null]" ID="6" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="6" RULES_CATEGORY_ID="[null]"
+ RULE_ID="[null]"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <!-- old measure data -->
+ <measure_data id="1" measure_id="1" snapshot_id="1" data="[null]"/>
+ <!--<measure_data id="2" measure_id="2" snapshot_id="2" data="[null]"/>-->
+ <!--<measure_data id="3" measure_id="3" snapshot_id="3" data="[null]"/>-->
+
+ <!-- last measure data -->
+ <measure_data id="4" measure_id="4" snapshot_id="4" data="[null]"/>
+ <measure_data id="5" measure_id="5" snapshot_id="5" data="[null]"/>
+ <measure_data id="6" measure_id="6" snapshot_id="6" data="[null]"/>
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeRuleMeasuresTest/purgeRuleMeasures.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeRuleMeasuresTest/purgeRuleMeasures.xml
new file mode 100644
index 00000000000..87f10a699e1
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeRuleMeasuresTest/purgeRuleMeasures.xml
@@ -0,0 +1,92 @@
+<dataset>
+
+ <!-- old snapshots -->
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="2" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="3" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+
+ <!-- last snapshots -->
+
+ <snapshots depth="[null]" id="4" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="5" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="4" root_project_id="[null]" root_snapshot_id="4" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="6" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="5" root_project_id="[null]" root_snapshot_id="4" status="P" islast="true"
+ path="[null]"/>
+
+ <!-- old measures -->
+ <project_measures characteristic_id="[null]" rule_priority="[null]" RULE_ID="[null]" RULES_CATEGORY_ID="[null]"
+ url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="4"
+ alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="[null]"
+ RULE_ID="[null]"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="[null]"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+ <!-- last measures -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="[null]"
+ RULE_ID="[null]"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="5" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="5" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="4"
+ alert_text="[null]" ID="6" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="6" RULES_CATEGORY_ID="[null]"
+ RULE_ID="[null]"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <!-- old measure data -->
+ <measure_data id="1" measure_id="1" snapshot_id="1" data="[null]"/>
+ <measure_data id="2" measure_id="2" snapshot_id="2" data="[null]"/>
+ <measure_data id="3" measure_id="3" snapshot_id="3" data="[null]"/>
+
+ <!-- last measure data -->
+ <measure_data id="4" measure_id="4" snapshot_id="4" data="[null]"/>
+ <measure_data id="5" measure_id="5" snapshot_id="5" data="[null]"/>
+ <measure_data id="6" measure_id="6" snapshot_id="6" data="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeRuleMeasuresTest/sharedFixture.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeRuleMeasuresTest/sharedFixture.xml
new file mode 100644
index 00000000000..d4e59f25a41
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeRuleMeasuresTest/sharedFixture.xml
@@ -0,0 +1,28 @@
+<dataset>
+ <rules_categories id="1" name="category one" description="[null]"/>
+ <rules id="1" name="foo" rules_category_id="1" plugin_config_key="checker/foo" plugin_rule_key="checkstyle.rule1"
+ plugin_name="maven-checkstyle-plugin" description="description" cardinality="SINGLE" parent_id="[null]"/>
+
+ <metrics id="1" name="ncloc" val_type="INT" description="[null]" domain="[null]"
+ short_name="" qualitative="false" user_managed="false" enabled="true" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/>
+
+ <!-- project -->
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- package -->
+ <projects long_name="[null]" id="2" scope="DIR" qualifier="PAC" kee="mygroup:myartifact:my.package" name="package"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- file -->
+ <projects long_name="[null]" id="3" scope="FIL" qualifier="CLA" kee="mygroup:myartifact:my.package.Class1"
+ name="class" root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeUnprocessedTest/purgeUnprocessed-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeUnprocessedTest/purgeUnprocessed-result.xml
new file mode 100644
index 00000000000..a7a4c7d364d
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeUnprocessedTest/purgeUnprocessed-result.xml
@@ -0,0 +1,141 @@
+<dataset>
+
+ <!-- valid snapshots -->
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="2" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="3" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="4" scope="FIL" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="4"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+
+ <!-- unprocessed snapshots -->
+
+ <!--<snapshots depth="[null]" id="5" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="1"-->
+ <!--parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="U" islast="false"-->
+ <!--path="[null]"/>-->
+
+ <!--<snapshots depth="[null]" id="6" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="2"-->
+ <!--parent_snapshot_id="5" root_project_id="[null]" root_snapshot_id="5" status="U" islast="false"-->
+ <!--path="[null]"/>-->
+
+ <!--<snapshots depth="[null]" id="7" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="3"-->
+ <!--parent_snapshot_id="6" root_project_id="[null]" root_snapshot_id="5" status="U" islast="false"-->
+ <!--path="[null]"/>-->
+
+ <!--<snapshots depth="[null]" id="8" scope="FIL" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"-->
+ <!--project_id="4"-->
+ <!--parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="U" islast="false"-->
+ <!--path="[null]"/>-->
+
+
+ <!-- valid source -->
+ <SNAPSHOT_SOURCES ID="1" SNAPSHOT_ID="3" DATA="foo"/>
+
+ <!-- unprocessed source -->
+ <!--<SNAPSHOT_SOURCES ID="2" SNAPSHOT_ID="7" DATA="foo"/>-->
+
+
+ <!-- valid violations -->
+ <RULE_FAILURES ID="1" SNAPSHOT_ID="1" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg1" LINE="[null]"/>
+ <RULE_FAILURES ID="2" SNAPSHOT_ID="2" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg2" LINE="[null]"/>
+ <RULE_FAILURES ID="3" SNAPSHOT_ID="3" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg3" LINE="[null]"/>
+ <RULE_FAILURES ID="4" SNAPSHOT_ID="4" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg4" LINE="[null]"/>
+
+
+ <!-- unprocessed violations -->
+ <!--<RULE_FAILURES ID="5" SNAPSHOT_ID="5" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg1" LINE="[null]"/>-->
+ <!--<RULE_FAILURES ID="6" SNAPSHOT_ID="6" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg2" LINE="[null]"/>-->
+ <!--<RULE_FAILURES ID="7" SNAPSHOT_ID="7" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg3" LINE="[null]"/>-->
+ <!--<RULE_FAILURES ID="8" SNAPSHOT_ID="8" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg4" LINE="[null]"/>-->
+
+ <!-- valid measures -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+ <!-- unprocessed measures -->
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="5" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="5" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="6" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="6" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="7" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="7" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+ <!--<project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"-->
+ <!--rule_priority="[null]"-->
+ <!--alert_text="[null]" ID="8" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="8" RULES_CATEGORY_ID="1"-->
+ <!--RULE_ID="1"-->
+ <!--text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"-->
+ <!--alert_status="[null]" description="[null]"/>-->
+
+
+ <!-- valid measure data -->
+ <measure_data id="1" measure_id="1" snapshot_id="1" data="[null]"/>
+ <measure_data id="2" measure_id="2" snapshot_id="2" data="[null]"/>
+ <measure_data id="3" measure_id="3" snapshot_id="3" data="[null]"/>
+ <measure_data id="4" measure_id="4" snapshot_id="4" data="[null]"/>
+
+ <!-- unprocessed measure data -->
+ <!--<measure_data id="5" measure_id="5" snapshot_id="5" data="[null]"/>-->
+ <!--<measure_data id="6" measure_id="6" snapshot_id="6" data="[null]"/>-->
+ <!--<measure_data id="7" measure_id="7" snapshot_id="7" data="[null]"/>-->
+ <!--<measure_data id="8" measure_id="8" snapshot_id="8" data="[null]"/>-->
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeUnprocessedTest/purgeUnprocessed.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeUnprocessedTest/purgeUnprocessed.xml
new file mode 100644
index 00000000000..c99d66a31a0
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeUnprocessedTest/purgeUnprocessed.xml
@@ -0,0 +1,141 @@
+<dataset>
+
+ <!-- valid snapshots -->
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="2" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="3" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="2" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="4" scope="FIL" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="4"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+
+ <!-- unprocessed snapshots -->
+
+ <snapshots depth="[null]" id="5" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="U" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="6" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="5" root_project_id="[null]" root_snapshot_id="5" status="U" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="7" scope="FIL" qualifier="CLA" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="3"
+ parent_snapshot_id="6" root_project_id="[null]" root_snapshot_id="5" status="U" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="8" scope="FIL" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="4"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="U" islast="false"
+ path="[null]"/>
+
+
+ <!-- valid source -->
+ <SNAPSHOT_SOURCES ID="1" SNAPSHOT_ID="3" DATA="foo"/>
+
+ <!-- unprocessed source -->
+ <SNAPSHOT_SOURCES ID="2" SNAPSHOT_ID="7" DATA="foo"/>
+
+
+ <!-- valid violations -->
+ <RULE_FAILURES ID="1" SNAPSHOT_ID="1" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg1" LINE="[null]"/>
+ <RULE_FAILURES ID="2" SNAPSHOT_ID="2" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg2" LINE="[null]"/>
+ <RULE_FAILURES ID="3" SNAPSHOT_ID="3" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg3" LINE="[null]"/>
+ <RULE_FAILURES ID="4" SNAPSHOT_ID="4" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg4" LINE="[null]"/>
+
+
+ <!-- unprocessed violations -->
+ <RULE_FAILURES ID="5" SNAPSHOT_ID="5" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg1" LINE="[null]"/>
+ <RULE_FAILURES ID="6" SNAPSHOT_ID="6" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg2" LINE="[null]"/>
+ <RULE_FAILURES ID="7" SNAPSHOT_ID="7" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg3" LINE="[null]"/>
+ <RULE_FAILURES ID="8" SNAPSHOT_ID="8" RULE_ID="1" FAILURE_LEVEL="2" MESSAGE="msg4" LINE="[null]"/>
+
+ <!-- valid measures -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="1" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="1" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="2" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="2" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="3" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="3" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="4" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="4" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+ <!-- unprocessed measures -->
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="5" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="5" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="6" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="6" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="7" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="7" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+ <project_measures characteristic_id="[null]" url="[null]" diff_value_1="[null]" diff_value_2="[null]" diff_value_3="[null]"
+ rule_priority="[null]"
+ alert_text="[null]" ID="8" VALUE="10.0" METRIC_ID="1" SNAPSHOT_ID="8" RULES_CATEGORY_ID="1"
+ RULE_ID="1"
+ text_value="[null]" tendency="[null]" measure_date="[null]" project_id="[null]"
+ alert_status="[null]" description="[null]"/>
+
+
+ <!-- valid measure data -->
+ <measure_data id="1" measure_id="1" snapshot_id="1" data="[null]"/>
+ <measure_data id="2" measure_id="2" snapshot_id="2" data="[null]"/>
+ <measure_data id="3" measure_id="3" snapshot_id="3" data="[null]"/>
+ <measure_data id="4" measure_id="4" snapshot_id="4" data="[null]"/>
+
+ <!-- unprocessed measure data -->
+ <measure_data id="5" measure_id="5" snapshot_id="5" data="[null]"/>
+ <measure_data id="6" measure_id="6" snapshot_id="6" data="[null]"/>
+ <measure_data id="7" measure_id="7" snapshot_id="7" data="[null]"/>
+ <measure_data id="8" measure_id="8" snapshot_id="8" data="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeUnprocessedTest/sharedFixture.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeUnprocessedTest/sharedFixture.xml
new file mode 100644
index 00000000000..8d4d27778c7
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/PurgeUnprocessedTest/sharedFixture.xml
@@ -0,0 +1,33 @@
+<dataset>
+ <rules_categories id="1" name="category one" description="[null]"/>
+ <rules id="1" name="foo" rules_category_id="1" plugin_config_key="checker/foo" plugin_rule_key="checkstyle.rule1"
+ plugin_name="maven-checkstyle-plugin" description="description" cardinality="SINGLE" parent_id="[null]"/>
+
+ <metrics id="1" name="ncloc" val_type="INT" description="[null]" domain="[null]"
+ short_name="" qualitative="false" user_managed="false" enabled="true" worst_value="[null]" optimized_best_value="[null]" best_value="[null]" direction="0" hidden="false"/>
+
+ <!-- project -->
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- package -->
+ <projects long_name="[null]" id="2" scope="DIR" qualifier="PAC" kee="mygroup:myartifact:my.package" name="package"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- file -->
+ <projects long_name="[null]" id="3" scope="FIL" qualifier="CLA" kee="mygroup:myartifact:my.package.Class1"
+ name="class" root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- project copy for views -->
+ <projects long_name="[null]" id="4" scope="FIL" qualifier="TRK" kee="cp-mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="1"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/UnflagLastDoublonsTest/sharedFixture.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/UnflagLastDoublonsTest/sharedFixture.xml
new file mode 100644
index 00000000000..ae4f6b24985
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/UnflagLastDoublonsTest/sharedFixture.xml
@@ -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
+ -->
+<dataset>
+
+ <!-- project -->
+ <projects long_name="[null]" id="1" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="project"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+ <!-- package -->
+ <projects long_name="[null]" id="2" scope="DIR" qualifier="PAC" kee="mygroup:myartifact:my.package" name="package"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"/>
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/UnflagLastDoublonsTest/unflagLastDoublons-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/UnflagLastDoublonsTest/unflagLastDoublons-result.xml
new file mode 100644
index 00000000000..e6a3e32080f
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/UnflagLastDoublonsTest/unflagLastDoublons-result.xml
@@ -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
+ -->
+<dataset>
+
+ <!-- first analysis, not flagged as last -->
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="2" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="1" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+
+ <!-- second analysis, flag islast => false -->
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2009-02-14 00:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="4" scope="DIR" qualifier="PAC" created_at="2009-02-14 00:00:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="3" root_project_id="[null]" root_snapshot_id="3" status="P" islast="false"
+ path="[null]"/>
+
+ <!-- third analysis, flag islast => false -->
+ <snapshots depth="[null]" id="5" scope="PRJ" qualifier="TRK" created_at="2009-04-01 00:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="6" scope="DIR" qualifier="PAC" created_at="2009-04-01 00:00:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="5" root_project_id="[null]" root_snapshot_id="5" status="P" islast="false"
+ path="[null]"/>
+
+
+ <!-- last analysis -->
+ <snapshots depth="[null]" id="7" scope="PRJ" qualifier="TRK" created_at="2009-05-18 00:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="8" scope="DIR" qualifier="PAC" created_at="2009-05-18 00:00:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="7" root_project_id="[null]" root_snapshot_id="7" status="P" islast="true"
+ path="[null]"/>
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/UnflagLastDoublonsTest/unflagLastDoublons.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/UnflagLastDoublonsTest/unflagLastDoublons.xml
new file mode 100644
index 00000000000..e8a0a7f145d
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/purges/UnflagLastDoublonsTest/unflagLastDoublons.xml
@@ -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
+ -->
+<dataset>
+
+ <!-- first analysis, not flagged as last -->
+
+ <snapshots depth="[null]" id="1" scope="PRJ" qualifier="TRK" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="2" scope="DIR" qualifier="PAC" created_at="2008-12-02 13:58:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="1" root_project_id="[null]" root_snapshot_id="1" status="P" islast="false"
+ path="[null]"/>
+
+
+ <!-- second analysis, crazy state : project not last, but package flagged as last -->
+ <snapshots depth="[null]" id="3" scope="PRJ" qualifier="TRK" created_at="2009-02-14 00:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="false"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="4" scope="DIR" qualifier="PAC" created_at="2009-02-14 00:00:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="3" root_project_id="[null]" root_snapshot_id="3" status="P" islast="true"
+ path="[null]"/>
+
+ <!-- third analysis, flagged as last -->
+ <snapshots depth="[null]" id="5" scope="PRJ" qualifier="TRK" created_at="2009-04-01 00:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="6" scope="DIR" qualifier="PAC" created_at="2009-04-01 00:00:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="5" root_project_id="[null]" root_snapshot_id="5" status="P" islast="true"
+ path="[null]"/>
+
+
+ <!-- last analysis -->
+ <snapshots depth="[null]" id="7" scope="PRJ" qualifier="TRK" created_at="2009-05-18 00:00:00.00" version="[null]"
+ project_id="1"
+ parent_snapshot_id="[null]" root_project_id="[null]" root_snapshot_id="[null]" status="P" islast="true"
+ path="[null]"/>
+
+ <snapshots depth="[null]" id="8" scope="DIR" qualifier="PAC" created_at="2009-05-18 00:00:00.00" version="[null]"
+ project_id="2"
+ parent_snapshot_id="7" root_project_id="[null]" root_snapshot_id="7" status="P" islast="true"
+ path="[null]"/>
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectAnyoneDefaultRoleToResource-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectAnyoneDefaultRoleToResource-result.xml
new file mode 100644
index 00000000000..28a4c30bb5c
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectAnyoneDefaultRoleToResource-result.xml
@@ -0,0 +1,21 @@
+<dataset>
+
+ <!-- GROUPS ***************** -->
+ <!-- global roles -->
+ <group_roles id="1" group_id="1" role="admin" resource_id="[null]" />
+ <group_roles id="2" group_id="2" role="viewer" resource_id="[null]" />
+
+ <!-- default project roles -->
+ <group_roles id="3" group_id="[null]" role="default-admin" resource_id="[null]" />
+ <group_roles id="4" group_id="[null]" role="default-viewer" resource_id="[null]" />
+
+ <!-- existing project roles -->
+ <group_roles id="5" group_id="5" role="admin" resource_id="7" />
+
+
+
+ <!-- new project role : group 'Anyone' has admin and viewer -->
+ <group_roles id="6" group_id="[null]" role="admin" resource_id="10" />
+ <group_roles id="7" group_id="[null]" role="viewer" resource_id="10" />
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectAnyoneDefaultRoleToResource.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectAnyoneDefaultRoleToResource.xml
new file mode 100644
index 00000000000..59af13b6b19
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectAnyoneDefaultRoleToResource.xml
@@ -0,0 +1,16 @@
+<dataset>
+
+ <!-- GROUPS ***************** -->
+ <!-- global roles -->
+ <group_roles id="1" group_id="1" role="admin" resource_id="[null]" />
+ <group_roles id="2" group_id="2" role="viewer" resource_id="[null]" />
+
+ <!-- default project roles -->
+ <group_roles id="3" group_id="[null]" role="default-admin" resource_id="[null]" />
+ <group_roles id="4" group_id="[null]" role="default-viewer" resource_id="[null]" />
+
+ <!-- existing project roles -->
+ <group_roles id="5" group_id="5" role="admin" resource_id="7" />
+
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectDefaultRolesToResource-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectDefaultRolesToResource-result.xml
new file mode 100644
index 00000000000..ecb82d497a1
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectDefaultRolesToResource-result.xml
@@ -0,0 +1,39 @@
+<dataset>
+
+ <!-- USERS ***************** -->
+
+ <!-- global roles -->
+ <user_roles id="1" user_id="1" role="admin" resource_id="[null]" />
+ <user_roles id="2" user_id="2" role="viewer" resource_id="[null]" />
+
+ <!-- default project roles -->
+ <user_roles id="3" user_id="1" role="default-admin" resource_id="[null]" />
+ <user_roles id="4" user_id="1" role="default-viewer" resource_id="[null]" />
+
+ <!-- existing project roles -->
+ <user_roles id="5" user_id="5" role="admin" resource_id="7" />
+
+
+ <!-- new project role -->
+ <user_roles id="6" user_id="1" role="admin" resource_id="10" />
+ <user_roles id="7" user_id="1" role="viewer" resource_id="10" />
+
+
+ <!-- GROUPS ***************** -->
+ <!-- global roles -->
+ <group_roles id="1" group_id="1" role="admin" resource_id="[null]" />
+ <group_roles id="2" group_id="2" role="viewer" resource_id="[null]" />
+
+ <!-- default project roles -->
+ <group_roles id="3" group_id="1" role="default-admin" resource_id="[null]" />
+ <group_roles id="4" group_id="1" role="default-viewer" resource_id="[null]" />
+
+ <!-- existing project roles -->
+ <group_roles id="5" group_id="5" role="admin" resource_id="7" />
+
+ <!-- new project roles -->
+ <group_roles id="6" group_id="1" role="admin" resource_id="10" />
+ <group_roles id="7" group_id="1" role="viewer" resource_id="10" />
+
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectDefaultRolesToResource.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectDefaultRolesToResource.xml
new file mode 100644
index 00000000000..a6565eeea01
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectDefaultRolesToResource.xml
@@ -0,0 +1,31 @@
+<dataset>
+
+ <!-- USERS ***************** -->
+
+ <!-- global roles -->
+ <user_roles id="1" user_id="1" role="admin" resource_id="[null]" />
+ <user_roles id="2" user_id="2" role="viewer" resource_id="[null]" />
+
+ <!-- default project roles -->
+ <user_roles id="3" user_id="1" role="default-admin" resource_id="[null]" />
+ <user_roles id="4" user_id="1" role="default-viewer" resource_id="[null]" />
+
+ <!-- existing project roles -->
+ <user_roles id="5" user_id="5" role="admin" resource_id="7" />
+
+
+
+ <!-- GROUPS ***************** -->
+ <!-- global roles -->
+ <group_roles id="1" group_id="1" role="admin" resource_id="[null]" />
+ <group_roles id="2" group_id="2" role="viewer" resource_id="[null]" />
+
+ <!-- default project roles -->
+ <group_roles id="3" group_id="1" role="default-admin" resource_id="[null]" />
+ <group_roles id="4" group_id="1" role="default-viewer" resource_id="[null]" />
+
+ <!-- existing project roles -->
+ <group_roles id="5" group_id="5" role="admin" resource_id="7" />
+
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectZeroDefaultRolesToResource-result.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectZeroDefaultRolesToResource-result.xml
new file mode 100644
index 00000000000..30ce5f48597
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectZeroDefaultRolesToResource-result.xml
@@ -0,0 +1,27 @@
+<dataset>
+
+ <!-- USERS ***************** -->
+
+ <!-- global roles -->
+ <user_roles id="1" user_id="1" role="admin" resource_id="[null]" />
+ <user_roles id="2" user_id="2" role="viewer" resource_id="[null]" />
+
+ <!-- existing project roles -->
+ <user_roles id="5" user_id="5" role="admin" resource_id="7" />
+
+ <!-- no default project roles -->
+
+
+
+ <!-- GROUPS ***************** -->
+
+ <!-- global roles -->
+ <group_roles id="1" group_id="1" role="admin" resource_id="[null]" />
+ <group_roles id="2" group_id="2" role="viewer" resource_id="[null]" />
+
+ <!-- existing project roles -->
+ <group_roles id="5" group_id="5" role="admin" resource_id="7" />
+
+ <!-- no default project roles -->
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectZeroDefaultRolesToResource.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectZeroDefaultRolesToResource.xml
new file mode 100644
index 00000000000..66f2aa19f2b
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/affectZeroDefaultRolesToResource.xml
@@ -0,0 +1,27 @@
+<dataset>
+
+ <!-- USERS ***************** -->
+
+ <!-- global roles -->
+ <user_roles id="1" user_id="1" role="admin" resource_id="[null]" />
+ <user_roles id="2" user_id="2" role="viewer" resource_id="[null]" />
+
+ <!-- existing project roles -->
+ <user_roles id="5" user_id="5" role="admin" resource_id="7" />
+
+ <!-- no default project roles -->
+
+
+
+ <!-- GROUPS ***************** -->
+
+ <!-- global roles -->
+ <group_roles id="1" group_id="1" role="admin" resource_id="[null]" />
+ <group_roles id="2" group_id="2" role="viewer" resource_id="[null]" />
+
+ <!-- existing project roles -->
+ <group_roles id="5" group_id="5" role="admin" resource_id="7" />
+
+ <!-- no default project roles -->
+
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/sharedFixture.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/sharedFixture.xml
new file mode 100644
index 00000000000..a9c48393d40
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/security/RoleManagerTest/sharedFixture.xml
@@ -0,0 +1,7 @@
+<dataset>
+ <projects id="10" scope="PRJ" qualifier="TRK" kee="mygroup:myartifact" name="[null]"
+ root_id="[null]"
+ description="[null]"
+ enabled="true" language="java" copy_resource_id="[null]"
+ long_name="[null]" />
+</dataset> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/ProjectLinksSensorTest/shouldDeleteMissingLinks.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/ProjectLinksSensorTest/shouldDeleteMissingLinks.xml
new file mode 100644
index 00000000000..836bb4630e9
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/ProjectLinksSensorTest/shouldDeleteMissingLinks.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar</artifactId>
+ <packaging>pom</packaging>
+ <version>1.8-SNAPSHOT</version>
+
+</project> \ No newline at end of file
diff --git a/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/ProjectLinksSensorTest/shouldSaveLinks.xml b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/ProjectLinksSensorTest/shouldSaveLinks.xml
new file mode 100644
index 00000000000..a44ea429019
--- /dev/null
+++ b/plugins/sonar-core-plugin/src/test/resources/org/sonar/plugins/core/sensors/ProjectLinksSensorTest/shouldSaveLinks.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar</artifactId>
+ <packaging>pom</packaging>
+ <version>1.8-SNAPSHOT</version>
+ <url>http://sonar.codehaus.org</url>
+
+ <organization>
+ <name>SonarSource SA</name>
+ <url>http://www.sonarsource.com</url>
+ </organization>
+ <inceptionYear>2009</inceptionYear>
+
+ <issueManagement>
+ <system>jira</system>
+ <url>http://jira.codehaus.org/browse/SONAR</url>
+ </issueManagement>
+
+ <mailingLists>
+ <mailingList>
+ <name>Sonar users mailing list</name>
+ <subscribe>http://xircles.codehaus.org/projects/sonar/lists</subscribe>
+ <unsubscribe>http://xircles.codehaus.org/projects/sonar/lists</unsubscribe>
+ <post>user@sonar.codehaus.org</post>
+ <archive>http://www.nabble.com/Sonar-f30151.html</archive>
+ </mailingList>
+ </mailingLists>
+
+ <scm>
+ <connection>scm:svn:http://svn.codehaus.org/sonar/trunk</connection>
+ <developerConnection>scm:svn:https://svn.codehaus.org/sonar/trunk</developerConnection>
+ <url>http://svn.sonar.codehaus.org</url>
+ </scm>
+
+ <ciManagement>
+ <system>bamboo</system>
+ <url>http://bamboo.ci.codehaus.org/browse/SONAR/</url>
+ </ciManagement>
+
+ <licenses>
+ <license>
+ <name>GNU Lesser General Public License (LGPL), v.3</name>
+ <url>http://www.gnu.org/licenses/lgpl.txt</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+
+</project> \ No newline at end of file