Explorar el Código

Merge branch Release-2.11

tags/2.11
GAUDIN hace 12 años
padre
commit
b5016f504b
Se han modificado 100 ficheros con 2169 adiciones y 681 borrados
  1. 1
    1
      archetypes/sonar-basic-plugin/pom.xml
  2. 1
    1
      archetypes/sonar-gwt-plugin/pom.xml
  3. 1
    1
      plugins/sonar-checkstyle-plugin/pom.xml
  4. 1
    1
      plugins/sonar-cobertura-plugin/pom.xml
  5. 1
    1
      plugins/sonar-core-gwt/pom.xml
  6. 15
    4
      plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsPanel.java
  7. 4
    4
      plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedRules.java
  8. 1
    1
      plugins/sonar-core-plugin/pom.xml
  9. 40
    28
      plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java
  10. 15
    11
      plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/batch/ExcludedResourceFilter.java
  11. 19
    0
      plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/ViolationTrackingDecorator.java
  12. 59
    0
      plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimeMachineWidget.java
  13. 53
    0
      plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimelineWidget.java
  14. 25
    19
      plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/custom_measures.html.erb
  15. 111
    0
      plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/time_machine.html.erb
  16. 162
    0
      plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/timeline.html.erb
  17. 13
    4
      plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/batch/ExcludedResourceFilterTest.java
  18. 12
    0
      plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/ViolationTrackingDecoratorTest.java
  19. 10
    1
      plugins/sonar-cpd-plugin/pom.xml
  20. 7
    52
      plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdAnalyser.java
  21. 38
    0
      plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdEngine.java
  22. 42
    20
      plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java
  23. 31
    65
      plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdSensor.java
  24. 112
    0
      plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/DuplicationsData.java
  25. 103
    0
      plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/PmdEngine.java
  26. 213
    0
      plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java
  27. 144
    0
      plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/DbDuplicationsIndex.java
  28. 81
    0
      plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/SonarDuplicationsIndex.java
  29. 2
    1
      plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdAnalyserTest.java
  30. 14
    34
      plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java
  31. 72
    0
      plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/PmdEngineTest.java
  32. 75
    0
      plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/index/DbDuplicationsIndexTest.java
  33. 47
    0
      plugins/sonar-cpd-plugin/src/test/resources/org/sonar/plugins/cpd/index/DbDuplicationsIndexTest/shouldGetByHash.xml
  34. 9
    0
      plugins/sonar-cpd-plugin/src/test/resources/org/sonar/plugins/cpd/index/DbDuplicationsIndexTest/shouldInsert-result.xml
  35. 7
    0
      plugins/sonar-cpd-plugin/src/test/resources/org/sonar/plugins/cpd/index/DbDuplicationsIndexTest/shouldInsert.xml
  36. 1
    1
      plugins/sonar-dbcleaner-plugin/pom.xml
  37. 18
    4
      plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/api/PurgeUtils.java
  38. 4
    1
      plugins/sonar-dbcleaner-plugin/src/test/resources/org/sonar/plugins/dbcleaner/api/PurgeUtilsTest/purgeSnapshots-result.xml
  39. 4
    1
      plugins/sonar-dbcleaner-plugin/src/test/resources/org/sonar/plugins/dbcleaner/api/PurgeUtilsTest/purgeSnapshots.xml
  40. 1
    1
      plugins/sonar-design-plugin/pom.xml
  41. 4
    12
      plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/DesignPlugin.java
  42. 1
    1
      plugins/sonar-email-notifications-plugin/pom.xml
  43. 1
    1
      plugins/sonar-findbugs-plugin/pom.xml
  44. 1
    1
      plugins/sonar-googleanalytics-plugin/pom.xml
  45. 1
    1
      plugins/sonar-l10n-en-plugin/pom.xml
  46. 70
    3
      plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties
  47. 1
    1
      plugins/sonar-pmd-plugin/pom.xml
  48. 1
    1
      plugins/sonar-squid-java-plugin/pom.xml
  49. 1
    1
      plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/bytecode/ClassworldsClassLoader.java
  50. 16
    3
      plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidPlugin.java
  51. 20
    0
      plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/bytecode/ClassworldsClassLoaderTest.java
  52. 1
    1
      plugins/sonar-surefire-plugin/pom.xml
  53. 1
    1
      plugins/sonar-surefire-plugin/src/main/java/org/sonar/plugins/surefire/api/AbstractSurefireParser.java
  54. 1
    1
      plugins/sonar-surefire-plugin/src/main/java/org/sonar/plugins/surefire/data/SurefireStaxHandler.java
  55. 13
    0
      plugins/sonar-surefire-plugin/src/test/java/org/sonar/plugins/surefire/api/AbstractSurefireParserTest.java
  56. 69
    0
      plugins/sonar-surefire-plugin/src/test/resources/org/sonar/plugins/surefire/api/AbstractSurefireParserTest/nestedInnerClasses/TEST-org.sonar.plugins.surefire.NestedTest$Inner1$Run.xml
  57. 67
    0
      plugins/sonar-surefire-plugin/src/test/resources/org/sonar/plugins/surefire/api/AbstractSurefireParserTest/nestedInnerClasses/TEST-org.sonar.plugins.surefire.NestedTest$Inner2$Run.xml
  58. 47
    13
      pom.xml
  59. 1
    1
      samples/pom.xml
  60. 1
    1
      sonar-application/pom.xml
  61. 17
    21
      sonar-application/src/main/assembly/conf/sonar.properties
  62. 1
    1
      sonar-batch-bootstrapper/pom.xml
  63. 1
    1
      sonar-batch-maven-compat/pom.xml
  64. 1
    1
      sonar-batch/pom.xml
  65. 8
    7
      sonar-batch/src/main/java/org/sonar/batch/AbstractMavenPluginExecutor.java
  66. 1
    1
      sonar-batch/src/main/java/org/sonar/batch/FakeMavenPluginExecutor.java
  67. 1
    1
      sonar-batch/src/main/java/org/sonar/batch/MavenPluginExecutor.java
  68. 20
    24
      sonar-batch/src/main/java/org/sonar/batch/ServerMetadata.java
  69. 7
    5
      sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionInstaller.java
  70. 1
    1
      sonar-batch/src/main/java/org/sonar/batch/index/EventPersister.java
  71. 5
    2
      sonar-batch/src/main/java/org/sonar/batch/phases/MavenPhaseExecutor.java
  72. 4
    4
      sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapModuleTest.java
  73. 13
    7
      sonar-batch/src/test/java/org/sonar/batch/phases/MavenPhaseExecutorTest.java
  74. 1
    1
      sonar-channel/pom.xml
  75. 1
    1
      sonar-check-api/pom.xml
  76. 1
    1
      sonar-colorizer/pom.xml
  77. 1
    35
      sonar-core-maven-plugin/pom.xml
  78. 2
    153
      sonar-core-maven-plugin/src/main/java/org/sonar/maven2/BatchMojo.java
  79. 0
    55
      sonar-core-maven-plugin/src/main/java/org/sonar/maven2/Maven2PluginExecutor.java
  80. 0
    23
      sonar-core-maven-plugin/src/test/resources/logback-text.xml
  81. 1
    1
      sonar-core/pom.xml
  82. 5
    0
      sonar-core/src/main/java/org/sonar/api/database/configuration/DatabaseConfiguration.java
  83. 2
    7
      sonar-core/src/main/java/org/sonar/api/database/configuration/Property.java
  84. 18
    21
      sonar-core/src/main/java/org/sonar/core/components/CacheRuleFinder.java
  85. 1
    1
      sonar-core/src/main/java/org/sonar/core/plugins/DefaultPluginMetadata.java
  86. 4
    0
      sonar-core/src/main/java/org/sonar/jpa/dialect/Derby.java
  87. 5
    0
      sonar-core/src/main/java/org/sonar/jpa/dialect/Dialect.java
  88. 4
    0
      sonar-core/src/main/java/org/sonar/jpa/dialect/HsqlDb.java
  89. 4
    0
      sonar-core/src/main/java/org/sonar/jpa/dialect/MsSql.java
  90. 4
    0
      sonar-core/src/main/java/org/sonar/jpa/dialect/MySql.java
  91. 4
    0
      sonar-core/src/main/java/org/sonar/jpa/dialect/Oracle.java
  92. 4
    0
      sonar-core/src/main/java/org/sonar/jpa/dialect/PostgreSql.java
  93. 100
    0
      sonar-core/src/main/java/org/sonar/jpa/entity/DuplicationBlock.java
  94. 8
    0
      sonar-core/src/main/java/org/sonar/jpa/entity/ManualMeasure.java
  95. 6
    0
      sonar-core/src/main/java/org/sonar/jpa/entity/NotificationQueueElement.java
  96. 11
    4
      sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java
  97. 2
    1
      sonar-core/src/main/resources/META-INF/persistence.xml
  98. 8
    0
      sonar-core/src/test/java/org/sonar/core/components/CacheRuleFinderTest.java
  99. 5
    1
      sonar-core/src/test/java/org/sonar/jpa/dialect/DialectRepositoryTest.java
  100. 0
    0
      sonar-deprecated/pom.xml

+ 1
- 1
archetypes/sonar-basic-plugin/pom.xml Ver fichero

@@ -5,7 +5,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>../..</relativePath>
</parent>


+ 1
- 1
archetypes/sonar-gwt-plugin/pom.xml Ver fichero

@@ -5,7 +5,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>../..</relativePath>
</parent>


+ 1
- 1
plugins/sonar-checkstyle-plugin/pom.xml Ver fichero

@@ -4,7 +4,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>../..</relativePath>
</parent>
<groupId>org.codehaus.sonar.plugins</groupId>

+ 1
- 1
plugins/sonar-cobertura-plugin/pom.xml Ver fichero

@@ -4,7 +4,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>../..</relativePath>
</parent>
<groupId>org.codehaus.sonar.plugins</groupId>

+ 1
- 1
plugins/sonar-core-gwt/pom.xml Ver fichero

@@ -4,7 +4,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>../..</relativePath>
</parent>
<artifactId>sonar-core-gwt</artifactId>

+ 15
- 4
plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/duplicationsviewer/client/DuplicationsPanel.java Ver fichero

@@ -91,13 +91,15 @@ public class DuplicationsPanel extends Composite {
panel.add(table);
int rowCounter = 1;

String projectKey = resource.getKey().substring(0, resource.getKey().lastIndexOf(':'));
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);
renderDuplication(rowCounter, i, table, lines, startLine, targetStartLine, targetResourceKey, resource, projectKey);
rowCounter+=2;
}
}
@@ -121,7 +123,7 @@ public class DuplicationsPanel extends Composite {
return table;
}

private void renderDuplication(int row, int duplicationCounter, FlexTable table, String lines, String startLine, String targetStartLine, String targetResourceKey, final Resource resource) {
private void renderDuplication(int row, int duplicationCounter, FlexTable table, String lines, String startLine, String targetStartLine, String targetResourceKey, final Resource resource, String projectKey) {
String style = (duplicationCounter % 2 == 0) ? "odd" : "even";
SourcePanel src = new DefaultSourcePanel(resource, new Integer(startLine), new Integer(lines));
@@ -137,9 +139,18 @@ public class DuplicationsPanel extends Composite {
targetResourceKey = "Same file";
}
if (targetResourceKey.contains(":")) {
targetResourceKey = targetResourceKey.substring(targetResourceKey.lastIndexOf(':') + 1);
int i = targetResourceKey.lastIndexOf(':');
String targetProjectKey = targetResourceKey.substring(0, i);
String targetFileKey = targetResourceKey.substring(i + 1);
if (targetProjectKey.equals(projectKey)) {
// same project
targetResourceKey = targetFileKey;
} else {
// another project
targetResourceKey = targetProjectKey + "<br/>" + targetFileKey;
}
}
table.setText(row, 3, targetResourceKey);
table.setHTML(row, 3, targetResourceKey);
table.setText(row, 4, targetStartLine);
setRowStyle(row, table, style, false);

+ 4
- 4
plugins/sonar-core-gwt/src/main/java/org/sonar/plugins/core/hotspots/client/widget/MostViolatedRules.java Ver fichero

@@ -91,7 +91,7 @@ public class MostViolatedRules extends AbstractHotspot {

@Override
protected void doOnResponse(Resource resource) {
if (resource.getMeasures().isEmpty()) {
if (resource==null || resource.getMeasures().isEmpty()) {
renderEmptyResults();
} else {
renderGrid(resource);
@@ -133,9 +133,9 @@ public class MostViolatedRules extends AbstractHotspot {
.setDepth(0)
.setExcludeRules(false)
.setLimit(LIMIT);
String priority = getSelectedPriority();
if (priority!=null) {
query.setRulePriorities(priority);
String severity = getSelectedPriority();
if (severity!=null) {
query.setRuleSeverities(severity);
}
return query;
}

+ 1
- 1
plugins/sonar-core-plugin/pom.xml Ver fichero

@@ -4,7 +4,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>../..</relativePath>
</parent>
<groupId>org.codehaus.sonar.plugins</groupId>

+ 40
- 28
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/CorePlugin.java Ver fichero

@@ -44,13 +44,22 @@ import org.sonar.plugins.core.widgets.*;
import java.util.List;

@Properties({
@Property(
key = CoreProperties.SERVER_BASE_URL,
defaultValue = CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE,
name = "Server base URL",
description = "HTTP URL of this Sonar server, such as <i>http://yourhost.yourdomain/sonar</i>. This value is used i.e. to create links in emails.",
project = false,
global = true,
category = CoreProperties.CATEGORY_GENERAL),
@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),
global = true,
category = CoreProperties.CATEGORY_CODE_COVERAGE),
@Property(
key = CoreProperties.CORE_IMPORT_SOURCES_PROPERTY,
defaultValue = "" + CoreProperties.CORE_IMPORT_SOURCES_DEFAULT_VALUE,
@@ -58,14 +67,16 @@ import java.util.List;
description = "Set to false if sources should not be displayed, e.g. for security reasons.",
project = true,
module = true,
global = true),
global = true,
category = CoreProperties.CATEGORY_SECURITY),
@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),
global = true,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
@Property(
key = CoreProperties.SKIP_TENDENCIES_PROPERTY,
defaultValue = "" + CoreProperties.SKIP_TENDENCIES_DEFAULT_VALUE,
@@ -73,56 +84,55 @@ import java.util.List;
description = "Skip calculation of measure tendencies",
project = true,
module = false,
global = true),
global = true,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
@Property(
key = CoreProperties.CORE_SKIPPED_MODULES_PROPERTY,
name = "Exclude modules",
description = "Maven artifact ids of modules to exclude (comma-separated).",
project = true,
global = false),
global = false,
category = CoreProperties.CATEGORY_GENERAL),
@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.SERVER_BASE_URL,
defaultValue = CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE,
name = "Server base URL",
description = "HTTP address of the Sonar server, such as <i>http://yourhost.yourdomain/sonar</i>. This value is used i.e. to create links in emails.",
project = false,
global = true),
global = true,
category = CoreProperties.CATEGORY_GENERAL),
@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),
global = true,
category = CoreProperties.CATEGORY_SECURITY),
@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),
global = true,
category = CoreProperties.CATEGORY_SECURITY),
@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
),
global = true,
category = CoreProperties.CATEGORY_SECURITY),
@Property(
key = CoreProperties.CORE_VIOLATION_LOCALE_PROPERTY,
defaultValue = "en",
name = "Locale used for violation messages",
description = "Locale to be used when generating violation messages. It's up to each rule engine to support this global internationalization property",
project = true,
global = true),
global = true,
category = CoreProperties.CATEGORY_L10N),
@Property(
key = "sonar.timemachine.period1",
name = "Period 1",
@@ -131,24 +141,24 @@ import java.util.List;
"compare to previous analysis</li><li>A version, for example 1.2</li></ul>",
project = false,
global = true,
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_1
),
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_1,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
@Property(
key = "sonar.timemachine.period2",
name = "Period 2",
description = "See the property 'Period 1'",
project = false,
global = true,
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_2
),
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_2,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
@Property(
key = "sonar.timemachine.period3",
name = "Period 3",
description = "See the property 'Period 1'",
project = false,
global = true,
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_3
),
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_3,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
@Property(
key = "sonar.timemachine.period4",
name = "Period 4",
@@ -157,16 +167,16 @@ import java.util.List;
"for example 2010-12-25</li><li>'previous_analysis' to compare to previous analysis</li><li>A version, for example 1.2</li></ul>",
project = true,
global = false,
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_4
),
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_4,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS),
@Property(
key = "sonar.timemachine.period5",
name = "Period 5",
description = "See the property 'Period 4'",
project = true,
global = false,
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_5
)
defaultValue = CoreProperties.TIMEMACHINE_DEFAULT_PERIOD_5,
category = CoreProperties.CATEGORY_DIFFERENTIAL_VIEWS)
})
public class CorePlugin extends SonarPlugin {

@@ -197,6 +207,8 @@ public class CorePlugin extends SonarPlugin {
extensions.add(SizeWidget.class);
extensions.add(EventsWidget.class);
extensions.add(CustomMeasuresWidget.class);
extensions.add(TimelineWidget.class);
extensions.add(TimeMachineWidget.class);

// chart
extensions.add(XradarChart.class);

+ 15
- 11
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/batch/ExcludedResourceFilter.java Ver fichero

@@ -19,8 +19,9 @@
*/
package org.sonar.plugins.core.batch;

import org.apache.commons.configuration.Configuration;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.ResourceFilter;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.api.resources.ResourceUtils;

@@ -29,14 +30,10 @@ import org.sonar.api.resources.ResourceUtils;
*/
public class ExcludedResourceFilter implements ResourceFilter {

private String[] exclusionPatterns;
private Configuration conf;

public ExcludedResourceFilter(Project project) {
this(project.getExclusionPatterns());
}

protected ExcludedResourceFilter(String[] exclusionPatterns) {
this.exclusionPatterns = (exclusionPatterns==null ? new String[0] : exclusionPatterns);
public ExcludedResourceFilter(Configuration conf) {
this.conf = conf;
}

public boolean isIgnored(Resource resource) {
@@ -45,11 +42,18 @@ public class ExcludedResourceFilter implements ResourceFilter {
return false;
}

for (String pattern : exclusionPatterns) {
if (resource.matchFilePattern(pattern)) {
return true;
String[] patterns = getExclusionPatterns();
if (patterns != null) {
for (String pattern : patterns) {
if (resource.matchFilePattern(pattern)) {
return true;
}
}
}
return false;
}

String[] getExclusionPatterns() {
return conf.getStringArray(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY);
}
}

+ 19
- 0
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/timemachine/ViolationTrackingDecorator.java Ver fichero

@@ -116,6 +116,16 @@ public class ViolationTrackingDecorator implements Decorator {
pastViolationsByRule, referenceViolationsMap);
}
}

// Last check: match violation if same rule and same checksum but different line and different message
// See https://jira.codehaus.org/browse/SONAR-2812
for (Violation newViolation : newViolations) {
if (isNotAlreadyMapped(newViolation, referenceViolationsMap)) {
mapViolation(newViolation,
findPastViolationWithSameChecksum(newViolation, pastViolationsByRule.get(newViolation.getRule().getId())),
pastViolationsByRule, referenceViolationsMap);
}
}
}
return referenceViolationsMap;
}
@@ -124,6 +134,15 @@ public class ViolationTrackingDecorator implements Decorator {
return !violationMap.containsKey(newViolation);
}

private RuleFailureModel findPastViolationWithSameChecksum(Violation newViolation, Collection<RuleFailureModel> pastViolations) {
for (RuleFailureModel pastViolation : pastViolations) {
if (isSameChecksum(newViolation, pastViolation)) {
return pastViolation;
}
}
return null;
}

private RuleFailureModel findPastViolationWithSameLineAndMessage(Violation newViolation, Collection<RuleFailureModel> pastViolations) {
for (RuleFailureModel pastViolation : pastViolations) {
if (isSameLine(newViolation, pastViolation) && isSameMessage(newViolation, pastViolation)) {

+ 59
- 0
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimeMachineWidget.java Ver fichero

@@ -0,0 +1,59 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2011 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* 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.widgets;

import org.sonar.api.web.AbstractRubyTemplate;
import org.sonar.api.web.RubyRailsWidget;
import org.sonar.api.web.WidgetCategory;
import org.sonar.api.web.WidgetProperties;
import org.sonar.api.web.WidgetProperty;
import org.sonar.api.web.WidgetPropertyType;

@WidgetCategory({ "History" })
@WidgetProperties(
{
@WidgetProperty(key = "numberOfColumns", type = WidgetPropertyType.INTEGER, defaultValue = "4"),
@WidgetProperty(key = "displaySparkLine", type = WidgetPropertyType.BOOLEAN),
@WidgetProperty(key = "metric1", type = WidgetPropertyType.METRIC, defaultValue = "ncloc"),
@WidgetProperty(key = "metric2", type = WidgetPropertyType.METRIC),
@WidgetProperty(key = "metric3", type = WidgetPropertyType.METRIC),
@WidgetProperty(key = "metric4", type = WidgetPropertyType.METRIC),
@WidgetProperty(key = "metric5", type = WidgetPropertyType.METRIC),
@WidgetProperty(key = "metric6", type = WidgetPropertyType.METRIC),
@WidgetProperty(key = "metric7", type = WidgetPropertyType.METRIC),
@WidgetProperty(key = "metric8", type = WidgetPropertyType.METRIC),
@WidgetProperty(key = "metric9", type = WidgetPropertyType.METRIC),
@WidgetProperty(key = "metric10", type = WidgetPropertyType.METRIC)
}
)
public class TimeMachineWidget extends AbstractRubyTemplate implements RubyRailsWidget {
public String getId() {
return "time_machine";
}

public String getTitle() {
return "History Table";
}

@Override
protected String getTemplatePath() {
return "/org/sonar/plugins/core/widgets/time_machine.html.erb";
}
}

+ 53
- 0
plugins/sonar-core-plugin/src/main/java/org/sonar/plugins/core/widgets/TimelineWidget.java Ver fichero

@@ -0,0 +1,53 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2011 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* 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.widgets;

import org.sonar.api.web.AbstractRubyTemplate;
import org.sonar.api.web.RubyRailsWidget;
import org.sonar.api.web.WidgetCategory;
import org.sonar.api.web.WidgetProperties;
import org.sonar.api.web.WidgetProperty;
import org.sonar.api.web.WidgetPropertyType;

@WidgetCategory({ "History" })
@WidgetProperties(
{
@WidgetProperty(key = "chartTitle", type = WidgetPropertyType.STRING),
@WidgetProperty(key = "metric1", type = WidgetPropertyType.METRIC, defaultValue = "ncloc"),
@WidgetProperty(key = "metric2", type = WidgetPropertyType.METRIC),
@WidgetProperty(key = "metric3", type = WidgetPropertyType.METRIC),
@WidgetProperty(key = "hideEvents", type = WidgetPropertyType.BOOLEAN),
@WidgetProperty(key = "chartHeight", type = WidgetPropertyType.INTEGER, defaultValue = "80")
}
)
public class TimelineWidget extends AbstractRubyTemplate implements RubyRailsWidget {
public String getId() {
return "timeline";
}

public String getTitle() {
return "Timeline";
}

@Override
protected String getTemplatePath() {
return "/org/sonar/plugins/core/widgets/timeline.html.erb";
}
}

+ 25
- 19
plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/custom_measures.html.erb Ver fichero

@@ -1,19 +1,25 @@
<%
(1..10).each do |index|
metric=widget_properties["metric#{index}"]
if metric
m=measure(metric)
if m
%>
<div class="dashbox">
<p class="title"><%= metric.short_name -%></p>
<p>
<span class="big"><%= format_measure(m, :url => url_for_drilldown(m)) -%></span>
<%= dashboard_configuration.selected_period? ? format_variation(m) : trend_icon(m) -%>
</p>
</div>
<%
end
end
end
%>
<table class="width100">
<tr>
<td width="100%">
<%
(1..10).each do |index|
metric=widget_properties["metric#{index}"]
if metric
m=measure(metric)
if m
%>
<div class="dashbox">
<p class="title"><%= metric.short_name -%></p>
<p>
<span class="big"><%= format_measure(m, :url => url_for_drilldown(m)) -%></span>
<%= dashboard_configuration.selected_period? ? format_variation(m) : trend_icon(m) -%>
</p>
</div>
<%
end
end
end
%>
</td>
</tr>
</table>

+ 111
- 0
plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/time_machine.html.erb Ver fichero

@@ -0,0 +1,111 @@
<%
# Retrieve widget settings
metric_ids = []
(1..10).each do |index|
metric=widget_properties["metric#{index}"]
if metric
metric_ids << metric.id
end
end
if metric_ids.empty?
# No metric has been selected, it's the first time the widget is displayed: 'ncloc' is the default metric
ncloc = Metric.find(:first, :conditions => "name = 'ncloc'")
metric_ids << ncloc.id
end
numberOfColumns = widget_properties["numberOfColumns"].to_i == 0 ? 4 : widget_properties["numberOfColumns"].to_i
displaySparkLine = widget_properties["displaySparkLine"]
# Retrieve the measures for each metric on each snapshot
options = {}
from_date = dashboard_configuration.from_datetime
if from_date
options[:from] = from_date
end
snapshots=Snapshot.for_timemachine_widget(@resource, numberOfColumns, options)
sids = snapshots.collect{|s| s.id}.uniq
measures=ProjectMeasure.find(:all, :conditions => {:snapshot_id => sids, :metric_id => metric_ids})

# And prepare the rows to display
snapshot_by_id={}
snapshots.each do |s|
snapshot_by_id[s.id]=s
end
rows_by_metric_id={}
measures.each do |measure|
next unless measure.metric

if measure.metric.timemachine? && (measure.value || measure.text_value)
row=rows_by_metric_id[measure.metric_id]
unless row
row=Sonar::TimemachineRow.new(measure.metric)
rows_by_metric_id[measure.metric_id]=row
end

#optimization : avoid eager loading of snapshots
measure.snapshot=snapshot_by_id[measure.snapshot_id]
row.add_measure(measure)
end
end
# Create the list of rows to display in the same order as defined by the user
rows=[]
metric_ids.each do |metric_id|
row = rows_by_metric_id[metric_id]
if row
rows<<row
end
end
%>

<div class="widget-matrix">

<table class="data">

<thead>
<tr>
<th> </th>
<%
snapshots.each do |snapshot|
event = snapshot.event('Version')
%>
<th nowrap="nowrap" style="vertical-align:top">
<%= l snapshot.created_at.to_date -%>
<br/>
<%= event.name unless event==nil -%>
</th>
<% end %>
<% if displaySparkLine %>
<th> </th>
<% end %>
</tr>
</thead>

<tbody>
<%
rows.select{|row| row.metric.val_type != Metric::VALUE_TYPE_DISTRIB}.each do |row|
%>
<tr class="<%= cycle 'even','odd' -%>">
<td width="1%" nowrap="nowrap" class="left text">
<%= row.metric.short_name %>
</td>
<%
snapshots.each do |snapshot|
measure=row.measure(snapshot)
%>
<td width="1%" nowrap="nowrap" class="right"><%= format_measure(measure, :skip_span_id => true) %></td>
<% end %>
<%
sparkline_url=row.sparkline_url
if displaySparkLine && sparkline_url
%>
<td width="1%" >
<%= image_tag(sparkline_url) %>
</td>
<% end %>
</tr>
<% end %>
</tbody>

</table>

</div>

+ 162
- 0
plugins/sonar-core-plugin/src/main/resources/org/sonar/plugins/core/widgets/timeline.html.erb Ver fichero

@@ -0,0 +1,162 @@
<%
# Retrieve widget settings
metric_data_map = {}
metric_name_map = {}
(1..3).each do |index|
metric=widget_properties["metric#{index}"]
if metric
metric_data_map[metric.id] = []
metric_name_map[metric.id] = metric.short_name
end
end
if metric_data_map.empty?
# No metric has been selected, it's the first time the widget is displayed: 'ncloc' is the default metric
ncloc = Metric.find(:first, :conditions => "name = 'ncloc'")
metric_data_map[ncloc.id] = []
metric_name_map[ncloc.id] = message('metric.ncloc.name')
end
chartHeight = widget_properties["chartHeight"].to_i == 0 ? "null" : widget_properties["chartHeight"]
# Retrieve metric trend information
options = {}
from_date = dashboard_configuration.from_datetime
if from_date
options[:from] = from_date
end
metric_count_per_snapshot_id = {}
TrendsChart.time_machine_measures(@resource, metric_data_map.keys, options).each() do |trend_item|
sid = trend_item["sid"]
if metric_count_per_snapshot_id[sid]
metric_count_per_snapshot_id[sid] += 1
else
metric_count_per_snapshot_id[sid] = 1
end
metric_data_map[trend_item["metric_id"].to_i] << {:date => trend_item["created_at"], :value => trend_item["value"], :sid => trend_item["sid"]}
end

# Create JS structures to print out in the HTML page
js_data = "["
js_snapshots = "["
js_metrics = "["
total_number_of_metrics = metric_name_map.keys.size()
metric_data_map.keys.each_with_index() do |metric_id, index|
unless metric_data_map[metric_id].empty?
js_metrics += "\"" + metric_name_map[metric_id] + "\","
js_data += "["
metric_data_map[metric_id].each() do |metric_data|
# for every metric value, we need to check that the corresponding snapshot has values for each metric (if not, Protovis won't be able to display)
if metric_count_per_snapshot_id[metric_data[:sid]]==total_number_of_metrics
m_date = Time.parse(metric_data[:date])
js_data += "{x:d("
js_data += m_date.year.to_s
js_data += ","
# Need to decrease by 1 the month as the JS Date object start months at 0 (= January)
js_data += (m_date.month - 1).to_s
js_data += ","
js_data += m_date.day.to_s
js_data += ","
js_data += m_date.hour.to_s
js_data += ","
js_data += m_date.min.to_s
js_data += ","
js_data += m_date.sec.to_s
js_data += "),y:"
js_data += sprintf( "%0.02f", metric_data[:value])
js_data += "},"
if index == 0
# we fill the js_snapshots array (no need to do this more than once)
js_snapshots += "{sid:"
js_snapshots += metric_data[:sid].to_s
js_snapshots += ",d:\""
js_snapshots += human_short_date m_date
js_snapshots += "\"},"
end
end
end
js_data = js_data.chomp(',') + "],"
end
end
js_data = js_data.chomp(',') + "]"
js_snapshots = js_snapshots.chomp(',') + "]"
js_metrics = js_metrics.chomp(',') + "]"
# Prepare also event structure if required
unless widget_properties["hideEvents"]
events = {}
unless from_date
# find the oldest date
metric_data_map.values.each() do |metric_data_array|
first_date = Time.parse(metric_data_array[0][:date])
from_date = first_date if !from_date || from_date > first_date
end
end
Event.find(:all, :conditions => ["resource_id=? AND event_date>=?", @resource.id, from_date], :order => 'event_date').each() do |event|
if events[event.event_date]
events[event.event_date] << event
else
date_entry = [event]
events[event.event_date] = date_entry
end
end
js_events = "["
events.keys().sort.each() do |e_date|
e_details = events[e_date]
js_events += "{sid:"
js_events += e_details[0].snapshot_id.to_s
js_events += ",d:d("
js_events += e_date.year.to_s
js_events += ","
# Need to decrease by 1 the month as the JS Date object start months at 0 (= January)
js_events += (e_date.month - 1).to_s
js_events += ","
js_events += e_date.day.to_s
js_events += ","
js_events += e_date.hour.to_s
js_events += ","
js_events += e_date.min.to_s
js_events += ","
js_events += e_date.sec.to_s
js_events += "),l:["
e_details.each() do |e|
js_events += "{n:\""
js_events += e.name
js_events += "\"},"
end
js_events = js_events.chomp(',') + "]},"
end
js_events = js_events.chomp(',') + "]"
end
%>

<% if widget_properties["chartTitle"] %>
<h3 style="text-align: center; margin-bottom: 10px"><%= h(widget_properties["chartTitle"]) -%></h3>
<% end %>


<% if metric_data_map.values[0].size == 1 %>

<span style="color: #777777; font-size: 93%; font-style:italic"><%= message('widget.timeline.timeline_not_displayed') -%></span>

<% else %>

<div id="timeline-chart-<%= widget.id -%>"></div>
<script type="text/javascript+protovis">
function d(y,m,d,h,min,s) {
return new Date(y,m,d,h,min,s);
}
var data = <%= js_data -%>;
var snapshots = <%= js_snapshots -%>;
var metrics = <%= js_metrics -%>;
var events = <%= js_events ? js_events : "null" -%>;
var timeline = new SonarWidgets.Timeline('timeline-chart-<%= widget.id -%>')
.height(<%= chartHeight -%>)
.data(data)
.snapshots(snapshots)
.metrics(metrics)
.events(events);
timeline.render();
</script>

<% end %>

+ 13
- 4
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/batch/ExcludedResourceFilterTest.java Ver fichero

@@ -19,7 +19,9 @@
*/
package org.sonar.plugins.core.batch;

import org.apache.commons.configuration.PropertiesConfiguration;
import org.junit.Test;
import org.sonar.api.CoreProperties;
import org.sonar.api.resources.Qualifiers;
import org.sonar.api.resources.Resource;

@@ -32,13 +34,16 @@ public class ExcludedResourceFilterTest {

@Test
public void doNotFailIfNoPatterns() {
ExcludedResourceFilter filter = new ExcludedResourceFilter((String[]) null);
PropertiesConfiguration conf = new PropertiesConfiguration();
ExcludedResourceFilter filter = new ExcludedResourceFilter(conf);
assertThat(filter.isIgnored(mock(Resource.class)), is(false));
}

@Test
public void noPatternsMatch() {
ExcludedResourceFilter filter = new ExcludedResourceFilter(new String[]{"**/foo/*.java", "**/bar/*"});
PropertiesConfiguration conf = new PropertiesConfiguration();
conf.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, new String[]{"**/foo/*.java", "**/bar/*"});
ExcludedResourceFilter filter = new ExcludedResourceFilter(conf);
assertThat(filter.isIgnored(mock(Resource.class)), is(false));
}

@@ -47,7 +52,9 @@ public class ExcludedResourceFilterTest {
*/
@Test
public void ignoreResourceIfMatchesPattern() {
ExcludedResourceFilter filter = new ExcludedResourceFilter(new String[]{"**/foo/*.java", "**/bar/*"});
PropertiesConfiguration conf = new PropertiesConfiguration();
conf.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, new String[]{"**/foo/*.java", "**/bar/*"});
ExcludedResourceFilter filter = new ExcludedResourceFilter(conf);

Resource resource = mock(Resource.class);
when(resource.matchFilePattern("**/bar/*")).thenReturn(true);
@@ -57,7 +64,9 @@ public class ExcludedResourceFilterTest {

@Test
public void doNotExcludeUnitTestFiles() {
ExcludedResourceFilter filter = new ExcludedResourceFilter(new String[]{"**/foo/*.java", "**/bar/*"});
PropertiesConfiguration conf = new PropertiesConfiguration();
conf.setProperty(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY, new String[]{"**/foo/*.java", "**/bar/*"});
ExcludedResourceFilter filter = new ExcludedResourceFilter(conf);

Resource unitTest = mock(Resource.class);
when(unitTest.getQualifier()).thenReturn(Qualifiers.UNIT_TEST_FILE);

+ 12
- 0
plugins/sonar-core-plugin/src/test/java/org/sonar/plugins/core/timemachine/ViolationTrackingDecoratorTest.java Ver fichero

@@ -102,6 +102,18 @@ public class ViolationTrackingDecoratorTest {
assertThat(mapping.get(newViolation), equalTo(referenceViolation));
}

/**
* See https://jira.codehaus.org/browse/SONAR-2812
*/
@Test
public void sameChecksumAndRuleButDifferentLineAndDifferentMessage() {
Violation newViolation = newViolation("new message", 1, 50, "checksum1");
RuleFailureModel referenceViolation = newReferenceViolation("old message", 2, 50, "checksum1");

Map<Violation, RuleFailureModel> mapping = decorator.mapViolations(Lists.newArrayList(newViolation), Lists.newArrayList(referenceViolation));
assertThat(mapping.get(newViolation), equalTo(referenceViolation));
}

@Test
public void shouldCreateNewViolationWhenSameRuleSameMessageButDifferentLineAndChecksum() {
Violation newViolation = newViolation("message", 1, 50, "checksum1");

+ 10
- 1
plugins/sonar-cpd-plugin/pom.xml Ver fichero

@@ -4,7 +4,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>../..</relativePath>
</parent>
<groupId>org.codehaus.sonar.plugins</groupId>
@@ -33,6 +33,15 @@
</exclusion>
</exclusions>
</dependency>

<!-- For ResourcePersister and database access -->
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-batch</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-duplications</artifactId>

+ 7
- 52
plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdAnalyser.java Ver fichero

@@ -19,20 +19,21 @@
*/
package org.sonar.plugins.cpd;

import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import net.sourceforge.pmd.cpd.TokenEntry;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.CpdMapping;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Measure;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.duplications.cpd.Match;

import java.io.File;
import java.util.*;

public class CpdAnalyser {

private static final Logger LOG = LoggerFactory.getLogger(CpdAnalyser.class);
@@ -83,7 +84,7 @@ public class CpdAnalyser {
}

for (DuplicationsData data : duplicationsData.values()) {
data.saveUsing(context);
data.save();
}
}

@@ -96,50 +97,4 @@ public class CpdAnalyser {
return data;
}

private static final class DuplicationsData {

protected Set<Integer> duplicatedLines = new HashSet<Integer>();
protected double duplicatedBlocks = 0;
protected Resource resource;
private SensorContext context;
private List<StringBuilder> duplicationXMLEntries = new ArrayList<StringBuilder>();

private DuplicationsData(Resource resource, SensorContext context) {
this.context = context;
this.resource = resource;
}

protected void cumulate(Resource targetResource, int targetDuplicationStartLine, int duplicationStartLine, int duplicatedLines) {
StringBuilder xml = new StringBuilder();
xml.append("<duplication lines=\"").append(duplicatedLines).append("\" start=\"").append(duplicationStartLine)
.append("\" target-start=\"").append(targetDuplicationStartLine).append("\" target-resource=\"")
.append(context.saveResource(targetResource)).append("\"/>");

duplicationXMLEntries.add(xml);

for (int duplicatedLine = duplicationStartLine; duplicatedLine < duplicationStartLine + duplicatedLines; duplicatedLine++) {
this.duplicatedLines.add(duplicatedLine);
}
}

protected void incrementDuplicatedBlock() {
duplicatedBlocks++;
}

protected void saveUsing(SensorContext context) {
context.saveMeasure(resource, CoreMetrics.DUPLICATED_FILES, 1d);
context.saveMeasure(resource, CoreMetrics.DUPLICATED_LINES, (double) duplicatedLines.size());
context.saveMeasure(resource, CoreMetrics.DUPLICATED_BLOCKS, duplicatedBlocks);
context.saveMeasure(resource, new Measure(CoreMetrics.DUPLICATIONS_DATA, getDuplicationXMLData()));
}

private String getDuplicationXMLData() {
StringBuilder duplicationXML = new StringBuilder("<duplications>");
for (StringBuilder xmlEntry : duplicationXMLEntries) {
duplicationXML.append(xmlEntry);
}
duplicationXML.append("</duplications>");
return duplicationXML.toString();
}
}
}

+ 38
- 0
plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdEngine.java Ver fichero

@@ -0,0 +1,38 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2011 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* 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.cpd;

import org.sonar.api.BatchExtension;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Project;

public abstract class CpdEngine implements BatchExtension {

abstract boolean isLanguageSupported(Language language);

abstract void analyse(Project project, SensorContext context);

@Override
public String toString() {
return getClass().getSimpleName();
}

}

+ 42
- 20
plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdPlugin.java Ver fichero

@@ -19,6 +19,9 @@
*/
package org.sonar.plugins.cpd;

import java.util.Arrays;
import java.util.List;

import org.sonar.api.CoreProperties;
import org.sonar.api.Properties;
import org.sonar.api.Property;
@@ -26,47 +29,66 @@ import org.sonar.api.SonarPlugin;
import org.sonar.plugins.cpd.decorators.DuplicationDensityDecorator;
import org.sonar.plugins.cpd.decorators.SumDuplicationsDecorator;

import java.util.Arrays;
import java.util.List;

@Properties({
@Property(
key = CoreProperties.CPD_ENGINE,
defaultValue = CoreProperties.CPD_ENGINE_DEFAULT_VALUE,
name = "Copy&Paste detection engine",
description = "Sonar embeds its own CPD engine since Sonar 2.11, but it's still possible to use the old PMD CPD engine (value 'pmd')." +
" Some Sonar users might want to keep on working with PMD CPD engine for instance to prevent any impact on measures during an upgrade of Sonar." +
" Moreover this Sonar CPD engine is not supported by all Sonar language plugins and when this support is not available," +
" the PMD CPD engine is automatically selected.",
project = true,
module = true,
global = true,
category = CoreProperties.CATEGORY_DUPLICATIONS),
@Property(
key = CoreProperties.CPD_CROSS_RPOJECT,
defaultValue = CoreProperties.CPD_CROSS_RPOJECT_DEFAULT_VALUE + "",
name = "Cross project duplicaton detection",
description = "Sonar supports the detection of cross project duplications." +
" Activating this property will slightly increase each Sonar analysis time." +
" This mode can't be used along with the PMD CPD engine.",
project = true,
module = true,
global = true,
category = CoreProperties.CATEGORY_DUPLICATIONS),
@Property(
key = CoreProperties.CPD_MINIMUM_TOKENS_PROPERTY,
defaultValue = CoreProperties.CPD_MINIMUM_TOKENS_DEFAULT_VALUE + "",
name = "Minimum tokens",
description = "The number of duplicate tokens above which a block is considered as a duplication.",
description = "Deprecated property used only by the PMD CPD engine." +
" The number of duplicate tokens above which a block is considered as a duplication.",
project = true,
module = true,
global = true),
global = true,
category = CoreProperties.CATEGORY_DUPLICATIONS),
@Property(
key = CoreProperties.CPD_IGNORE_LITERALS_PROPERTY,
defaultValue = CoreProperties.CPD_IGNORE_LITERALS_DEFAULT_VALUE + "",
name = "Ignore literals",
description = "if true, CPD ignores literal value differences when evaluating a duplicate block. " +
"This means that foo=\"first string\"; and foo=\"second string\"; will be seen as equivalent.",
description = "Deprecated property used only by the PMD CPD engine." +
" If true, PMD-CPD ignores literal value differences when evaluating a duplicate block." +
" This means that foo=\"first string\"; and foo=\"second string\"; will be seen as equivalent.",
project = true,
module = true,
global = true),
global = true,
category = CoreProperties.CATEGORY_DUPLICATIONS),
@Property(
key = CoreProperties.CPD_IGNORE_IDENTIFIERS_PROPERTY,
defaultValue = CoreProperties.CPD_IGNORE_IDENTIFIERS_DEFAULT_VALUE + "",
name = "Ignore identifiers",
description = "Similar to 'Ignore literals' but for identifiers; i.e., variable names, methods names, and so forth.",
description = "Deprecated property used only by the PMD CPD engine." +
" Similar to 'Ignore literals' but for identifiers; i.e., variable names, methods names, and so forth.",
project = true,
module = true,
global = true),
@Property(
key = CoreProperties.CPD_SKIP_PROPERTY,
defaultValue = "false",
name = "Skip detection of duplicated code",
description = "Searching for duplicated code is memory hungry therefore for very big projects it can be necessary to turn the functionality off.",
project = true,
module = true,
global = true)
global = true,
category = CoreProperties.CATEGORY_DUPLICATIONS)
})
public class CpdPlugin extends SonarPlugin {

public List getExtensions() {
return Arrays.asList(CpdSensor.class, SumDuplicationsDecorator.class, DuplicationDensityDecorator.class, JavaCpdMapping.class);
return Arrays.asList(CpdSensor.class, SumDuplicationsDecorator.class, DuplicationDensityDecorator.class, JavaCpdMapping.class, SonarEngine.class, PmdEngine.class);
}
}

}

+ 31
- 65
plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/CpdSensor.java Ver fichero

@@ -19,101 +19,67 @@
*/
package org.sonar.plugins.cpd;

import net.sourceforge.pmd.cpd.AbstractLanguage;
import net.sourceforge.pmd.cpd.TokenEntry;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.lang.StringUtils;
import org.slf4j.LoggerFactory;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.CpdMapping;
import org.sonar.api.batch.Sensor;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Project;
import org.sonar.duplications.cpd.CPD;

import java.io.IOException;
import java.nio.charset.Charset;
import org.sonar.api.utils.Logs;

public class CpdSensor implements Sensor {

private CpdMapping[] mappings;
private CpdEngine sonarEngine;
private CpdEngine pmdEngine;

public CpdSensor(CpdMapping[] mappings) {
this.mappings = mappings;
public CpdSensor(SonarEngine sonarEngine, PmdEngine pmdEngine) {
this.sonarEngine = sonarEngine;
this.pmdEngine = pmdEngine;
}

public boolean shouldExecuteOnProject(Project project) {
CpdMapping mapping = getMapping(project.getLanguage());
if (mapping == null) {
LoggerFactory.getLogger(getClass()).info("Detection of duplication code is not supported for {}.", project.getLanguage());
if (isSkipped(project)) {
LoggerFactory.getLogger(getClass()).info("Detection of duplicated code is skipped");
return false;
}

if (isSkipped(project)) {
LoggerFactory.getLogger(getClass()).info("Detection of duplicated code is skipped");
if (!getEngine(project).isLanguageSupported(project.getLanguage())) {
LoggerFactory.getLogger(getClass()).info("Detection of duplication code is not supported for {}.", project.getLanguage());
return false;
}

return true;
}

boolean isSkipped(Project project) {
Configuration conf = project.getConfiguration();
return conf.getBoolean("sonar.cpd." + project.getLanguageKey() + ".skip",
conf.getBoolean("sonar.cpd.skip", false));
}

public void analyse(Project project, SensorContext context) {
CpdMapping mapping = getMapping(project.getLanguage());
CPD cpd = executeCPD(project, mapping, project.getFileSystem().getSourceCharset());
saveResults(cpd, mapping, project, context);
}

private CpdMapping getMapping(Language language) {
for (CpdMapping cpdMapping : mappings) {
if (cpdMapping.getLanguage().equals(language)) {
return cpdMapping;
private CpdEngine getEngine(Project project) {
if (isSonarEngineEnabled(project)) {
if (sonarEngine.isLanguageSupported(project.getLanguage())) {
return sonarEngine;
} else {
// fallback to PMD
return pmdEngine;
}
} else {
return pmdEngine;
}
return null;
}

private void saveResults(CPD cpd, CpdMapping mapping, Project project, SensorContext context) {
CpdAnalyser cpdAnalyser = new CpdAnalyser(project, context, mapping);
cpdAnalyser.analyse(cpd.getMatches());
}

private CPD executeCPD(Project project, CpdMapping mapping, Charset encoding) {
try {
CPD cpd = configureCPD(project, mapping, encoding);
cpd.go();
return cpd;

} catch (Exception e) {
throw new CpdException(e);
}
boolean isSonarEngineEnabled(Project project) {
Configuration conf = project.getConfiguration();
return StringUtils.equalsIgnoreCase(conf.getString(CoreProperties.CPD_ENGINE, CoreProperties.CPD_ENGINE_DEFAULT_VALUE), "sonar");
}

private CPD configureCPD(Project project, CpdMapping mapping, Charset encoding) throws IOException {
// To avoid a cpd bug generating error as "java.lang.IndexOutOfBoundsException: Index: 259, Size: 248"
// See http://sourceforge.net/tracker/?func=detail&atid=479921&aid=1947823&group_id=56262 for more details
TokenEntry.clearImages();

int minTokens = getMinimumTokens(project);
AbstractLanguage cpdLanguage = new AbstractLanguage(mapping.getTokenizer()) {
};

CPD cpd = new CPD(minTokens, cpdLanguage);
cpd.setEncoding(encoding.name());
cpd.setLoadSourceCodeSlices(false);
cpd.add(project.getFileSystem().getSourceFiles(project.getLanguage()));
return cpd;
boolean isSkipped(Project project) {
Configuration conf = project.getConfiguration();
return conf.getBoolean("sonar.cpd." + project.getLanguageKey() + ".skip",
conf.getBoolean(CoreProperties.CPD_SKIP_PROPERTY, false));
}

int getMinimumTokens(Project project) {
Configuration conf = project.getConfiguration();
return conf.getInt("sonar.cpd." + project.getLanguageKey() + ".minimumTokens",
conf.getInt("sonar.cpd.minimumTokens", CoreProperties.CPD_MINIMUM_TOKENS_DEFAULT_VALUE));
public void analyse(Project project, SensorContext context) {
CpdEngine engine = getEngine(project);
Logs.INFO.info("{} is used", engine);
engine.analyse(project, context);
}

@Override

+ 112
- 0
plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/DuplicationsData.java Ver fichero

@@ -0,0 +1,112 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2011 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* 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.cpd;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.sonar.api.batch.SensorContext;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.measures.Measure;
import org.sonar.api.resources.Resource;

public class DuplicationsData {

private Resource resource;
private Set<Integer> duplicatedLines = new HashSet<Integer>();
private double duplicatedBlocks;
private List<XmlEntry> duplicationXMLEntries = new ArrayList<XmlEntry>();

private SensorContext context;

public DuplicationsData(Resource resource, SensorContext context) {
this.resource = resource;
this.context = context;
}

public void cumulate(String targetResource, int targetDuplicationStartLine, int duplicationStartLine, int duplicatedLines) {
duplicationXMLEntries.add(new XmlEntry(targetResource, targetDuplicationStartLine, duplicationStartLine, duplicatedLines));
for (int duplicatedLine = duplicationStartLine; duplicatedLine < duplicationStartLine + duplicatedLines; duplicatedLine++) {
this.duplicatedLines.add(duplicatedLine);
}
}

public void cumulate(Resource targetResource, int targetDuplicationStartLine, int duplicationStartLine, int duplicatedLines) {
cumulate(context.saveResource(targetResource), targetDuplicationStartLine, duplicationStartLine, duplicatedLines);
}

public void incrementDuplicatedBlock() {
duplicatedBlocks++;
}

public void save() {
context.saveMeasure(resource, CoreMetrics.DUPLICATED_FILES, 1d);
context.saveMeasure(resource, CoreMetrics.DUPLICATED_LINES, (double) duplicatedLines.size());
context.saveMeasure(resource, CoreMetrics.DUPLICATED_BLOCKS, duplicatedBlocks);
context.saveMeasure(resource, new Measure(CoreMetrics.DUPLICATIONS_DATA, getDuplicationXMLData()));
}

private String getDuplicationXMLData() {
Collections.sort(duplicationXMLEntries, COMPARATOR);
StringBuilder duplicationXML = new StringBuilder("<duplications>");
for (XmlEntry xmlEntry : duplicationXMLEntries) {
duplicationXML.append(xmlEntry.toString());
}
duplicationXML.append("</duplications>");
return duplicationXML.toString();
}

private static final Comparator<XmlEntry> COMPARATOR = new Comparator<XmlEntry>() {
public int compare(XmlEntry o1, XmlEntry o2) {
if (o1.startLine == o2.startLine) {
return o1.lines - o2.lines;
}
return o1.startLine - o2.startLine;
}
};

private static final class XmlEntry {
private String target;
private int targetStartLine;
private int startLine;
private int lines;

private XmlEntry(String target, int targetStartLine, int startLine, int lines) {
this.target = target;
this.targetStartLine = targetStartLine;
this.startLine = startLine;
this.lines = lines;
}

@Override
public String toString() {
return new StringBuilder().append("<duplication lines=\"").append(lines)
.append("\" start=\"").append(startLine)
.append("\" target-start=\"").append(targetStartLine)
.append("\" target-resource=\"").append(target).append("\"/>")
.toString();
}
}

}

+ 103
- 0
plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/PmdEngine.java Ver fichero

@@ -0,0 +1,103 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2011 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* 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.cpd;

import java.io.IOException;
import java.nio.charset.Charset;

import net.sourceforge.pmd.cpd.AbstractLanguage;
import net.sourceforge.pmd.cpd.TokenEntry;

import org.apache.commons.configuration.Configuration;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.CpdMapping;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.resources.Language;
import org.sonar.api.resources.Project;
import org.sonar.duplications.cpd.CPD;

public class PmdEngine extends CpdEngine {

private CpdMapping[] mappings;

public PmdEngine(CpdMapping[] mappings) {
this.mappings = mappings;
}

@Override
public boolean isLanguageSupported(Language language) {
return getMapping(language) != null;
}

private CpdMapping getMapping(Language language) {
for (CpdMapping cpdMapping : mappings) {
if (cpdMapping.getLanguage().equals(language)) {
return cpdMapping;
}
}
return null;
}

@Override
public void analyse(Project project, SensorContext context) {
CpdMapping mapping = getMapping(project.getLanguage());
CPD cpd = executeCPD(project, mapping, project.getFileSystem().getSourceCharset());
saveResults(cpd, mapping, project, context);
}

private void saveResults(CPD cpd, CpdMapping mapping, Project project, SensorContext context) {
CpdAnalyser cpdAnalyser = new CpdAnalyser(project, context, mapping);
cpdAnalyser.analyse(cpd.getMatches());
}

private CPD executeCPD(Project project, CpdMapping mapping, Charset encoding) {
try {
CPD cpd = configureCPD(project, mapping, encoding);
cpd.go();
return cpd;

} catch (Exception e) {
throw new CpdException(e);
}
}

private CPD configureCPD(Project project, CpdMapping mapping, Charset encoding) throws IOException {
// To avoid a cpd bug generating error as "java.lang.IndexOutOfBoundsException: Index: 259, Size: 248"
// See http://sourceforge.net/tracker/?func=detail&atid=479921&aid=1947823&group_id=56262 for more details
TokenEntry.clearImages();

int minTokens = getMinimumTokens(project);
AbstractLanguage cpdLanguage = new AbstractLanguage(mapping.getTokenizer()) {
};

CPD cpd = new CPD(minTokens, cpdLanguage);
cpd.setEncoding(encoding.name());
cpd.setLoadSourceCodeSlices(false);
cpd.add(project.getFileSystem().getSourceFiles(project.getLanguage()));
return cpd;
}

int getMinimumTokens(Project project) {
Configuration conf = project.getConfiguration();
return conf.getInt("sonar.cpd." + project.getLanguageKey() + ".minimumTokens",
conf.getInt("sonar.cpd.minimumTokens", CoreProperties.CPD_MINIMUM_TOKENS_DEFAULT_VALUE));
}

}

+ 213
- 0
plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/SonarEngine.java Ver fichero

@@ -0,0 +1,213 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2011 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* 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.cpd;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.*;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.SensorContext;
import org.sonar.api.database.DatabaseSession;
import org.sonar.api.database.model.ResourceModel;
import org.sonar.api.resources.*;
import org.sonar.api.utils.Logs;
import org.sonar.api.utils.SonarException;
import org.sonar.batch.index.ResourcePersister;
import org.sonar.duplications.block.Block;
import org.sonar.duplications.block.BlockChunker;
import org.sonar.duplications.detector.original.OriginalCloneDetectionAlgorithm;
import org.sonar.duplications.index.CloneGroup;
import org.sonar.duplications.index.CloneIndex;
import org.sonar.duplications.index.ClonePart;
import org.sonar.duplications.java.JavaStatementBuilder;
import org.sonar.duplications.java.JavaTokenProducer;
import org.sonar.duplications.statement.Statement;
import org.sonar.duplications.statement.StatementChunker;
import org.sonar.duplications.token.TokenChunker;
import org.sonar.plugins.cpd.index.DbDuplicationsIndex;
import org.sonar.plugins.cpd.index.SonarDuplicationsIndex;

public class SonarEngine extends CpdEngine {

private static final int BLOCK_SIZE = 10;

/**
* Limit of time to analyse one file (in seconds).
*/
private static final int TIMEOUT = 5 * 60;

private final ResourcePersister resourcePersister;
private final DatabaseSession dbSession;

/**
* For dry run, where is no access to database.
*/
public SonarEngine() {
this(null, null);
}

public SonarEngine(ResourcePersister resourcePersister, DatabaseSession dbSession) {
this.resourcePersister = resourcePersister;
this.dbSession = dbSession;
}

@Override
public boolean isLanguageSupported(Language language) {
return Java.INSTANCE.equals(language);
}

/**
* @return true, if was enabled by user and database is available
*/
private boolean isCrossProject(Project project) {
return project.getConfiguration().getBoolean(CoreProperties.CPD_CROSS_RPOJECT, CoreProperties.CPD_CROSS_RPOJECT_DEFAULT_VALUE)
&& resourcePersister != null && dbSession != null
&& StringUtils.isBlank(project.getConfiguration().getString(CoreProperties.PROJECT_BRANCH_PROPERTY));
}

private static String getFullKey(Project project, Resource resource) {
return new StringBuilder(ResourceModel.KEY_SIZE)
.append(project.getKey())
.append(':')
.append(resource.getKey())
.toString();
}

@Override
public void analyse(Project project, SensorContext context) {
List<InputFile> inputFiles = project.getFileSystem().mainFiles(project.getLanguageKey());
if (inputFiles.isEmpty()) {
return;
}

// Create index
final SonarDuplicationsIndex index;
if (isCrossProject(project)) {
Logs.INFO.info("Cross-project analysis enabled");
index = new SonarDuplicationsIndex(new DbDuplicationsIndex(dbSession, resourcePersister, project));
} else {
Logs.INFO.info("Cross-project analysis disabled");
index = new SonarDuplicationsIndex();
}

TokenChunker tokenChunker = JavaTokenProducer.build();
StatementChunker statementChunker = JavaStatementBuilder.build();
BlockChunker blockChunker = new BlockChunker(BLOCK_SIZE);

for (InputFile inputFile : inputFiles) {
Resource resource = getResource(inputFile);
String resourceKey = getFullKey(project, resource);

List<Statement> statements;

Reader reader = null;
try {
reader = new InputStreamReader(new FileInputStream(inputFile.getFile()), project.getFileSystem().getSourceCharset());
statements = statementChunker.chunk(tokenChunker.chunk(reader));
} catch (FileNotFoundException e) {
throw new SonarException(e);
} finally {
IOUtils.closeQuietly(reader);
}

List<Block> blocks = blockChunker.chunk(resourceKey, statements);
index.insert(resource, blocks);
}

// Detect
ExecutorService executorService = Executors.newSingleThreadExecutor();
try {
for (InputFile inputFile : inputFiles) {
Logs.INFO.debug("Detection of duplications for {}", inputFile.getFile());
Resource resource = getResource(inputFile);
String resourceKey = getFullKey(project, resource);

Collection<Block> fileBlocks = index.getByResource(resource, resourceKey);

List<CloneGroup> clones;
try {
clones = executorService.submit(new Task(index, fileBlocks)).get(TIMEOUT, TimeUnit.SECONDS);
} catch (TimeoutException e) {
clones = null;
Logs.INFO.warn("Timeout during detection of duplications for " + inputFile.getFile(), e);
} catch (InterruptedException e) {
throw new SonarException(e);
} catch (ExecutionException e) {
throw new SonarException(e);
}

if (clones != null && !clones.isEmpty()) {
// Save
DuplicationsData data = new DuplicationsData(resource, context);
for (CloneGroup clone : clones) {
poplulateData(data, clone);
}
data.save();
}
}
} finally {
executorService.shutdown();
}
}

private static class Task implements Callable<List<CloneGroup>> {
private final CloneIndex index;
private final Collection<Block> fileBlocks;

public Task(CloneIndex index, Collection<Block> fileBlocks) {
this.index = index;
this.fileBlocks = fileBlocks;
}

public List<CloneGroup> call() {
return OriginalCloneDetectionAlgorithm.detect(index, fileBlocks);
}
}

private Resource getResource(InputFile inputFile) {
return JavaFile.fromRelativePath(inputFile.getRelativePath(), false);
}

private void poplulateData(DuplicationsData data, CloneGroup clone) {
ClonePart origin = clone.getOriginPart();
int originLines = origin.getLineEnd() - origin.getLineStart() + 1;

data.incrementDuplicatedBlock();
for (ClonePart part : clone.getCloneParts()) {
if (part.equals(origin)) {
continue;
}
data.cumulate(part.getResourceId(), part.getLineStart(), origin.getLineStart(), originLines);

if (part.getResourceId().equals(origin.getResourceId())) {
data.incrementDuplicatedBlock();
data.cumulate(origin.getResourceId(), origin.getLineStart(), part.getLineStart(), originLines);
}
}
}

}

+ 144
- 0
plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/DbDuplicationsIndex.java Ver fichero

@@ -0,0 +1,144 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2011 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* 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.cpd.index;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import javax.persistence.Query;

import org.hibernate.ejb.HibernateQuery;
import org.hibernate.transform.Transformers;
import org.sonar.api.database.DatabaseSession;
import org.sonar.api.database.model.Snapshot;
import org.sonar.api.resources.Project;
import org.sonar.api.resources.Resource;
import org.sonar.batch.index.ResourcePersister;
import org.sonar.duplications.block.Block;
import org.sonar.duplications.block.ByteArray;
import org.sonar.jpa.entity.DuplicationBlock;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

public class DbDuplicationsIndex {

private final Map<ByteArray, Collection<Block>> cache = Maps.newHashMap();

private final DatabaseSession session;
private final ResourcePersister resourcePersister;
private final int currentProjectSnapshotId;
private final Integer lastSnapshotId;

public DbDuplicationsIndex(DatabaseSession session, ResourcePersister resourcePersister, Project currentProject) {
this.session = session;
this.resourcePersister = resourcePersister;
Snapshot currentSnapshot = resourcePersister.getSnapshotOrFail(currentProject);
Snapshot lastSnapshot = resourcePersister.getLastSnapshot(currentSnapshot, false);
this.currentProjectSnapshotId = currentSnapshot.getId();
this.lastSnapshotId = lastSnapshot == null ? null : lastSnapshot.getId();
}

/**
* For tests.
*/
DbDuplicationsIndex(DatabaseSession session, ResourcePersister resourcePersister, Integer currentProjectSnapshotId, Integer prevSnapshotId) {
this.session = session;
this.resourcePersister = resourcePersister;
this.currentProjectSnapshotId = currentProjectSnapshotId;
this.lastSnapshotId = prevSnapshotId;
}

int getSnapshotIdFor(Resource resource) {
return resourcePersister.getSnapshotOrFail(resource).getId();
}

public void prepareCache(Resource resource) {
int resourceSnapshotId = getSnapshotIdFor(resource);

// Order of columns is important - see code below!
String sql = "SELECT DISTINCT to_blocks.hash, res.kee, to_blocks.index_in_file, to_blocks.start_line, to_blocks.end_line" +
" FROM duplications_index to_blocks, duplications_index from_blocks, snapshots snapshot, projects res" +
" WHERE from_blocks.snapshot_id = :resource_snapshot_id" +
" AND to_blocks.hash = from_blocks.hash" +
" AND to_blocks.snapshot_id = snapshot.id" +
" AND snapshot.islast = :is_last" +
" AND snapshot.project_id = res.id";
if (lastSnapshotId != null) {
// Filter for blocks from previous snapshot of current project
sql += " AND to_blocks.project_snapshot_id != :last_project_snapshot_id";
}
Query query = session.getEntityManager().createNativeQuery(sql)
.setParameter("resource_snapshot_id", resourceSnapshotId)
.setParameter("is_last", Boolean.TRUE);
if (lastSnapshotId != null) {
query.setParameter("last_project_snapshot_id", lastSnapshotId);
}
// Ugly hack for mapping results of custom SQL query into plain list (MyBatis is coming soon)
((HibernateQuery) query).getHibernateQuery().setResultTransformer(Transformers.TO_LIST);
List<List<Object>> blocks = query.getResultList();

cache.clear();
for (List<Object> dbBlock : blocks) {
String hash = (String) dbBlock.get(0);
String resourceKey = (String) dbBlock.get(1);
int indexInFile = ((Number) dbBlock.get(2)).intValue();
int startLine = ((Number) dbBlock.get(3)).intValue();
int endLine = ((Number) dbBlock.get(4)).intValue();

Block block = new Block(resourceKey, new ByteArray(hash), indexInFile, startLine, endLine);

// Group blocks by hash
Collection<Block> sameHash = cache.get(block.getBlockHash());
if (sameHash == null) {
sameHash = Lists.newArrayList();
cache.put(block.getBlockHash(), sameHash);
}
sameHash.add(block);
}
}

public Collection<Block> getByHash(ByteArray hash) {
Collection<Block> result = cache.get(hash);
if (result != null) {
return result;
} else {
return Collections.emptyList();
}
}

public void insert(Resource resource, Collection<Block> blocks) {
int resourceSnapshotId = getSnapshotIdFor(resource);
for (Block block : blocks) {
DuplicationBlock dbBlock = new DuplicationBlock(
currentProjectSnapshotId,
resourceSnapshotId,
block.getBlockHash().toString(),
block.getIndexInFile(),
block.getFirstLineNumber(),
block.getLastLineNumber());
session.save(dbBlock);
}
session.commit();
}

}

+ 81
- 0
plugins/sonar-cpd-plugin/src/main/java/org/sonar/plugins/cpd/index/SonarDuplicationsIndex.java Ver fichero

@@ -0,0 +1,81 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2011 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* 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.cpd.index;

import java.util.Collection;
import java.util.List;

import org.sonar.api.resources.Resource;
import org.sonar.duplications.block.Block;
import org.sonar.duplications.block.ByteArray;
import org.sonar.duplications.index.AbstractCloneIndex;
import org.sonar.duplications.index.CloneIndex;
import org.sonar.duplications.index.PackedMemoryCloneIndex;

import com.google.common.collect.Lists;

public class SonarDuplicationsIndex extends AbstractCloneIndex {

private final CloneIndex mem = new PackedMemoryCloneIndex();
private final DbDuplicationsIndex db;

public SonarDuplicationsIndex() {
this(null);
}

public SonarDuplicationsIndex(DbDuplicationsIndex db) {
this.db = db;
}

public void insert(Resource resource, Collection<Block> blocks) {
for (Block block : blocks) {
mem.insert(block);
}
if (db != null) {
db.insert(resource, blocks);
}
}

public Collection<Block> getByResource(Resource resource, String resourceKey) {
if (db != null) {
db.prepareCache(resource);
}
return mem.getByResourceId(resourceKey);
}

public Collection<Block> getBySequenceHash(ByteArray hash) {
if (db == null) {
return mem.getBySequenceHash(hash);
} else {
List<Block> result = Lists.newArrayList(mem.getBySequenceHash(hash));
result.addAll(db.getByHash(hash));
return result;
}
}

public Collection<Block> getByResourceId(String resourceId) {
throw new UnsupportedOperationException();
}

public void insert(Block block) {
throw new UnsupportedOperationException();
}

}

+ 2
- 1
plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdAnalyserTest.java Ver fichero

@@ -130,8 +130,9 @@ public class CpdAnalyserTest {
verify(context).saveMeasure(
eq(resource1),
argThat(new IsMeasure(CoreMetrics.DUPLICATIONS_DATA, "<duplications>"
+ "<duplication lines=\"100\" start=\"5\" target-start=\"15\" target-resource=\"key3\"/>"
+ "<duplication lines=\"200\" start=\"5\" target-start=\"15\" target-resource=\"key2\"/>"
+ "<duplication lines=\"100\" start=\"5\" target-start=\"15\" target-resource=\"key3\"/>" + "</duplications>")));
+ "</duplications>")));

verify(context).saveMeasure(resource2, CoreMetrics.DUPLICATED_FILES, 1d);
verify(context).saveMeasure(resource2, CoreMetrics.DUPLICATED_LINES, 200d);

+ 14
- 34
plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/CpdSensorTest.java Ver fichero

@@ -19,16 +19,16 @@
*/
package org.sonar.plugins.cpd;

import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

import org.apache.commons.configuration.PropertiesConfiguration;
import org.junit.Test;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.CpdMapping;
import org.sonar.api.resources.Project;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;

public class CpdSensorTest {

@Test
@@ -38,7 +38,7 @@ public class CpdSensorTest {

Project project = createJavaProject().setConfiguration(conf);

CpdSensor sensor = new CpdSensor(new CpdMapping[0]);
CpdSensor sensor = new CpdSensor(new SonarEngine(), new PmdEngine(new CpdMapping[0]));
assertTrue(sensor.isSkipped(project));
}

@@ -46,7 +46,7 @@ public class CpdSensorTest {
public void doNotSkipByDefault() {
Project project = createJavaProject().setConfiguration(new PropertiesConfiguration());

CpdSensor sensor = new CpdSensor(new CpdMapping[0]);
CpdSensor sensor = new CpdSensor(new SonarEngine(), new PmdEngine(new CpdMapping[0]));
assertFalse(sensor.isSkipped(project));
}

@@ -59,41 +59,20 @@ public class CpdSensorTest {
Project phpProject = createPhpProject().setConfiguration(conf);
Project javaProject = createJavaProject().setConfiguration(conf);

CpdSensor sensor = new CpdSensor(new CpdMapping[0]);
CpdSensor sensor = new CpdSensor(new SonarEngine(), new PmdEngine(new CpdMapping[0]));
assertTrue(sensor.isSkipped(phpProject));
assertFalse(sensor.isSkipped(javaProject));
}

@Test
public void defaultMinimumTokens() {
Project project = createJavaProject().setConfiguration(new PropertiesConfiguration());

CpdSensor sensor = new CpdSensor(new CpdMapping[0]);
assertEquals(CoreProperties.CPD_MINIMUM_TOKENS_DEFAULT_VALUE, sensor.getMinimumTokens(project));
}

@Test
public void generalMinimumTokens() {
public void engine() {
PropertiesConfiguration conf = new PropertiesConfiguration();
conf.setProperty("sonar.cpd.minimumTokens", "33");
Project project = createJavaProject().setConfiguration(conf);
CpdSensor sensor = new CpdSensor(new SonarEngine(), new PmdEngine(new CpdMapping[0]));

CpdSensor sensor = new CpdSensor(new CpdMapping[0]);
assertEquals(33, sensor.getMinimumTokens(project));
}

@Test
public void minimumTokensByLanguage() {
PropertiesConfiguration conf = new PropertiesConfiguration();
conf.setProperty("sonar.cpd.minimumTokens", "100");
conf.setProperty("sonar.cpd.php.minimumTokens", "33");

Project phpProject = createPhpProject().setConfiguration(conf);
Project javaProject = createJavaProject().setConfiguration(conf);

CpdSensor sensor = new CpdSensor(new CpdMapping[0]);
assertEquals(100, sensor.getMinimumTokens(javaProject));
assertEquals(33, sensor.getMinimumTokens(phpProject));
assertThat(sensor.isSonarEngineEnabled(project), is(true));
conf.setProperty("sonar.cpd.engine", "pmd");
assertThat(sensor.isSonarEngineEnabled(project), is(false));
}

private Project createJavaProject() {
@@ -103,4 +82,5 @@ public class CpdSensorTest {
private Project createPhpProject() {
return new Project("php_project").setLanguageKey("php");
}

}

+ 72
- 0
plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/PmdEngineTest.java Ver fichero

@@ -0,0 +1,72 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2011 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* 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.cpd;

import static junit.framework.Assert.assertEquals;

import org.apache.commons.configuration.PropertiesConfiguration;
import org.junit.Test;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.CpdMapping;
import org.sonar.api.resources.Project;

public class PmdEngineTest {

@Test
public void defaultMinimumTokens() {
Project project = createJavaProject().setConfiguration(new PropertiesConfiguration());

PmdEngine sensor = new PmdEngine(new CpdMapping[0]);
assertEquals(CoreProperties.CPD_MINIMUM_TOKENS_DEFAULT_VALUE, sensor.getMinimumTokens(project));
}

@Test
public void generalMinimumTokens() {
PropertiesConfiguration conf = new PropertiesConfiguration();
conf.setProperty("sonar.cpd.minimumTokens", "33");
Project project = createJavaProject().setConfiguration(conf);

PmdEngine sensor = new PmdEngine(new CpdMapping[0]);
assertEquals(33, sensor.getMinimumTokens(project));
}

@Test
public void minimumTokensByLanguage() {
PropertiesConfiguration conf = new PropertiesConfiguration();
conf.setProperty("sonar.cpd.minimumTokens", "100");
conf.setProperty("sonar.cpd.php.minimumTokens", "33");

Project phpProject = createPhpProject().setConfiguration(conf);
Project javaProject = createJavaProject().setConfiguration(conf);

PmdEngine sensor = new PmdEngine(new CpdMapping[0]);
assertEquals(100, sensor.getMinimumTokens(javaProject));
assertEquals(33, sensor.getMinimumTokens(phpProject));
}

private Project createJavaProject() {
return new Project("java_project").setLanguageKey("java");
}

private Project createPhpProject() {
return new Project("php_project").setLanguageKey("php");
}

}

+ 75
- 0
plugins/sonar-cpd-plugin/src/test/java/org/sonar/plugins/cpd/index/DbDuplicationsIndexTest.java Ver fichero

@@ -0,0 +1,75 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2011 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* 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.cpd.index;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

import org.junit.Test;
import org.sonar.api.resources.JavaFile;
import org.sonar.api.resources.Resource;
import org.sonar.duplications.block.Block;
import org.sonar.duplications.block.ByteArray;
import org.sonar.jpa.test.AbstractDbUnitTestCase;

public class DbDuplicationsIndexTest extends AbstractDbUnitTestCase {

private DbDuplicationsIndex index;

@Test
public void shouldGetByHash() {
Resource resource = new JavaFile("foo");
index = spy(new DbDuplicationsIndex(getSession(), null, 9, 7));
doReturn(10).when(index).getSnapshotIdFor(resource);
setupData("shouldGetByHash");

index.prepareCache(resource);
Collection<Block> blocks = index.getByHash(new ByteArray("aa"));
Iterator<Block> blocksIterator = blocks.iterator();

assertThat(blocks.size(), is(1));

Block block = blocksIterator.next();
assertThat("block resourceId", block.getResourceId(), is("bar-last"));
assertThat("block hash", block.getBlockHash(), is(new ByteArray("aa")));
assertThat("block index in file", block.getIndexInFile(), is(0));
assertThat("block start line", block.getFirstLineNumber(), is(1));
assertThat("block end line", block.getLastLineNumber(), is(2));
}

@Test
public void shouldInsert() {
Resource resource = new JavaFile("foo");
index = spy(new DbDuplicationsIndex(getSession(), null, 1, null));
doReturn(2).when(index).getSnapshotIdFor(resource);
setupData("shouldInsert");

index.insert(resource, Arrays.asList(new Block("foo", new ByteArray("bb"), 0, 1, 2)));

checkTables("shouldInsert", "duplications_index");
}

}

+ 47
- 0
plugins/sonar-cpd-plugin/src/test/resources/org/sonar/plugins/cpd/index/DbDuplicationsIndexTest/shouldGetByHash.xml Ver fichero

@@ -0,0 +1,47 @@
<dataset>

<snapshots id="1" status="P" islast="false" project_id="0" />
<snapshots id="2" status="P" islast="false" project_id="1" />
<projects id="1" kee="bar-old" enabled="true" scope="FIL" qualifier="CLA" />

<snapshots id="3" status="P" islast="true" />
<snapshots id="4" status="P" islast="true" project_id="2" />
<projects id="2" kee="bar-last" enabled="true" scope="FIL" qualifier="CLA" />

<snapshots id="5" status="P" islast="false" />
<snapshots id="6" status="P" islast="false" project_id="3" />
<projects id="3" kee="foo-old" enabled="true" scope="FIL" qualifier="CLA" />

<snapshots id="7" status="P" islast="true" />
<snapshots id="8" status="P" islast="true" project_id="4" />
<projects id="4" kee="foo-last" enabled="true" scope="FIL" qualifier="CLA" />

<snapshots id="9" status="U" islast="false" />
<snapshots id="10" status="U" islast="false" project_id="5" />
<projects id="5" kee="foo" enabled="true" scope="FIL" qualifier="CLA" />

<!-- Old snapshot of another project -->
<!-- bar-old -->
<duplications_index id="1" project_snapshot_id="1" snapshot_id="2" hash="bb" index_in_file="0" start_line="0" end_line="0" />

<!-- Last snapshot of another project -->
<!-- bar-last -->
<duplications_index id="2" project_snapshot_id="3" snapshot_id="4" hash="aa" index_in_file="0" start_line="1" end_line="2" />

<!-- Old snapshot of current project -->
<!-- foo-old -->
<duplications_index id="3" project_snapshot_id="5" snapshot_id="6" hash="bb" index_in_file="0" start_line="0" end_line="0" />

<!-- Last snapshot of current project -->
<!-- foo-last -->
<duplications_index id="4" project_snapshot_id="7" snapshot_id="8" hash="bb" index_in_file="0" start_line="0" end_line="0" />

<!-- New snapshot of current project -->
<!-- foo -->
<duplications_index id="5" project_snapshot_id="9" snapshot_id="10" hash="aa" index_in_file="0" start_line="0" end_line="0" />

<!-- Note that there is two blocks with same hash for current analysis to verify that we use "SELECT DISTINCT", -->
<!-- without "DISTINCT" we will select block from "bar-last" two times. -->
<duplications_index id="6" project_snapshot_id="9" snapshot_id="10" hash="aa" index_in_file="1" start_line="1" end_line="1" />

</dataset>

+ 9
- 0
plugins/sonar-cpd-plugin/src/test/resources/org/sonar/plugins/cpd/index/DbDuplicationsIndexTest/shouldInsert-result.xml Ver fichero

@@ -0,0 +1,9 @@
<dataset>

<snapshots id="1" status="U" islast="false" project_id="0" />
<snapshots id="2" status="U" islast="false" project_id="1" />
<projects id="1" kee="foo" enabled="true" scope="FIL" qualifier="CLA" />

<duplications_index id="1" project_snapshot_id="1" snapshot_id="2" hash="bb" index_in_file="0" start_line="1" end_line="2" />

</dataset>

+ 7
- 0
plugins/sonar-cpd-plugin/src/test/resources/org/sonar/plugins/cpd/index/DbDuplicationsIndexTest/shouldInsert.xml Ver fichero

@@ -0,0 +1,7 @@
<dataset>

<snapshots id="1" status="U" islast="false" project_id="0" />
<snapshots id="2" status="U" islast="false" project_id="1" />
<projects id="1" kee="foo" enabled="true" scope="FIL" qualifier="CLA" />

</dataset>

+ 1
- 1
plugins/sonar-dbcleaner-plugin/pom.xml Ver fichero

@@ -4,7 +4,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>../..</relativePath>
</parent>
<groupId>org.codehaus.sonar.plugins</groupId>

+ 18
- 4
plugins/sonar-dbcleaner-plugin/src/main/java/org/sonar/plugins/dbcleaner/api/PurgeUtils.java Ver fichero

@@ -19,14 +19,20 @@
*/
package org.sonar.plugins.dbcleaner.api;

import java.util.List;

import javax.persistence.Query;

import org.apache.commons.configuration.Configuration;
import org.sonar.api.database.DatabaseSession;
import org.sonar.api.database.model.*;
import org.sonar.api.database.model.MeasureData;
import org.sonar.api.database.model.MeasureModel;
import org.sonar.api.database.model.RuleFailureModel;
import org.sonar.api.database.model.Snapshot;
import org.sonar.api.database.model.SnapshotSource;
import org.sonar.api.design.DependencyDto;
import org.sonar.api.utils.TimeProfiler;

import javax.persistence.Query;
import java.util.List;
import org.sonar.jpa.entity.DuplicationBlock;

/**
* @since 2.5
@@ -58,6 +64,7 @@ public final class PurgeUtils {
deleteSources(session, snapshotIds);
deleteViolations(session, snapshotIds);
deleteDependencies(session, snapshotIds);
deleteDuplicationBlocks(session, snapshotIds);
deleteSnapshots(session, snapshotIds);
}

@@ -96,6 +103,13 @@ public final class PurgeUtils {
executeQuery(session, "delete violations", snapshotIds, "delete from " + RuleFailureModel.class.getSimpleName() + " e where e.snapshotId in (:ids)");
}

/**
* @since 2.11
*/
private static void deleteDuplicationBlocks(DatabaseSession session, List<Integer> snapshotIds) {
executeQuery(session, "delete duplication blocks", snapshotIds, "delete from " + DuplicationBlock.class.getSimpleName() + " e where e.snapshotId in (:ids)");
}

/**
* Delete SNAPSHOTS table
*/

+ 4
- 1
plugins/sonar-dbcleaner-plugin/src/test/resources/org/sonar/plugins/dbcleaner/api/PurgeUtilsTest/purgeSnapshots-result.xml Ver fichero

@@ -108,4 +108,7 @@
<!--parent_dependency_id="[null]" project_snapshot_id="1"-->
<!--dep_usage="INHERITS" dep_weight="1" from_scope="FIL" to_scope="FIL"/>-->

</dataset>
<!--<duplications_index id="1" project_snapshot_id="1" snapshot_id="3" hash="bb" index_in_file="0" start_line="0" end_line="0" />-->
<!--<duplications_index id="2" project_snapshot_id="1" snapshot_id="4" hash="bb" index_in_file="0" start_line="0" end_line="0" />-->

</dataset>

+ 4
- 1
plugins/sonar-dbcleaner-plugin/src/test/resources/org/sonar/plugins/dbcleaner/api/PurgeUtilsTest/purgeSnapshots.xml Ver fichero

@@ -108,4 +108,7 @@
parent_dependency_id="[null]" project_snapshot_id="1"
dep_usage="INHERITS" dep_weight="1" from_scope="FIL" to_scope="FIL" />

</dataset>
<duplications_index id="1" project_snapshot_id="1" snapshot_id="3" hash="bb" index_in_file="0" start_line="0" end_line="0" />
<duplications_index id="2" project_snapshot_id="1" snapshot_id="4" hash="bb" index_in_file="0" start_line="0" end_line="0" />

</dataset>

+ 1
- 1
plugins/sonar-design-plugin/pom.xml Ver fichero

@@ -4,7 +4,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>../..</relativePath>
</parent>
<groupId>org.codehaus.sonar.plugins</groupId>

+ 4
- 12
plugins/sonar-design-plugin/src/main/java/org/sonar/plugins/design/DesignPlugin.java Ver fichero

@@ -19,7 +19,8 @@
*/
package org.sonar.plugins.design;

import org.sonar.api.*;
import com.google.common.collect.Lists;
import org.sonar.api.SonarPlugin;
import org.sonar.plugins.design.batch.*;
import org.sonar.plugins.design.ui.dependencies.GwtDependenciesTab;
import org.sonar.plugins.design.ui.lcom4.GwtLcom4Tab;
@@ -29,21 +30,12 @@ import org.sonar.plugins.design.ui.widgets.ChidamberKemererWidget;
import org.sonar.plugins.design.ui.widgets.FileDesignWidget;
import org.sonar.plugins.design.ui.widgets.PackageDesignWidget;

import java.util.ArrayList;
import java.util.List;

@Properties({
@Property(
key = CoreProperties.DESIGN_SKIP_DESIGN_PROPERTY,
defaultValue = "" + CoreProperties.DESIGN_SKIP_DESIGN_DEFAULT_VALUE,
name = "Skip design analysis",
project = true,
global = true)
})
public class DesignPlugin extends SonarPlugin {

public List<Class<? extends Extension>> getExtensions() {
List<Class<? extends Extension>> extensions = new ArrayList<Class<? extends Extension>>();
public List getExtensions() {
List extensions = Lists.newArrayList();

// Batch
extensions.add(MavenDependenciesSensor.class);

+ 1
- 1
plugins/sonar-email-notifications-plugin/pom.xml Ver fichero

@@ -5,7 +5,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>../..</relativePath>
</parent>


+ 1
- 1
plugins/sonar-findbugs-plugin/pom.xml Ver fichero

@@ -4,7 +4,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>../..</relativePath>
</parent>
<groupId>org.codehaus.sonar.plugins</groupId>

+ 1
- 1
plugins/sonar-googleanalytics-plugin/pom.xml Ver fichero

@@ -5,7 +5,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>../..</relativePath>
</parent>


+ 1
- 1
plugins/sonar-l10n-en-plugin/pom.xml Ver fichero

@@ -5,7 +5,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>../..</relativePath>
</parent>


+ 70
- 3
plugins/sonar-l10n-en-plugin/src/main/resources/org/sonar/l10n/core.properties Ver fichero

@@ -21,6 +21,7 @@ backup_verb=Backup
blocker=Blocker
bold=Bold
build_date=Build date
build_time=Build time
cancel=Cancel
category=Category
calendar=Calendar
@@ -278,6 +279,10 @@ manual_measures.page=Manual Measures
my_profile.page=My Profile
project_roles.page=Project Roles
project_settings.page=Settings
project_links.page=Links
project_exclusions.page=Exclusions
project_history.page=History Deletion
project_deletion.page=Project Deletion
quality_profiles.page=Quality Profiles
reviews.page=Reviews
settings.page=General Settings
@@ -375,6 +380,7 @@ reviews.without_false_positives=Without false positives
reviews.showing_false_positives_only=Showing false positives only
reviews.why_false_positive=Why is it a false-positive ?
reviews.why_not_false_positive=Why is it not a false-positive anymore ?
reviews.user_does_not_exist=\ : user does not exist. \\nPlease select a valid user or leave the field blank.


#------------------------------------------------------------------------------
@@ -414,6 +420,23 @@ dashboard.edit_dashboard=Edit dashboard
dashboard.update_dashboard=Update dashboard


#------------------------------------------------------------------------------
#
# SETTINGS
#
#------------------------------------------------------------------------------
settings.save_category=Save {0} Settings
property.category.email=Email
property.category.general=General
property.category.security=Security
property.category.java=Java
property.category.differentialViews=Differential Views
property.category.codeCoverage=Code Coverage
property.category.duplications=Duplications
property.category.localization=Localization
property.category.server_id=Server ID


#------------------------------------------------------------------------------
#
# WIDGETS
@@ -494,6 +517,13 @@ widget.size.methods.suffix=\ methods
widget.size.accessors.suffix=\ accessors
widget.size.paragraphs.suffix=\ paragraphs

widget.timeline.name=Timeline
widget.timeline.description=Displays up to 3 metrics on a history chart.
widget.timeline.timeline_not_displayed=The timeline won't be displayed as currently only 1 snapshot is available.

widget.time_machine.name=History Table
widget.time_machine.description=Displays up to 10 metrics in a table, showing their value for a specified number of past snapshots.

widget.ckjm.name=Chidamber & Kemerer
widget.ckjm.description=Reports on LCOM4 and RFC average and distribution.
widget.ckjm.lcom4=LCOM4
@@ -567,6 +597,25 @@ manual_measures.save_and_add_button=Save & Add new
manual_measures.pending_message=Pending measures are marked with orange box. Their values will be integrated to project during next analysis.


#------------------------------------------------------------------------------
#
# PROJECT HISTORY SERVICE
#
#------------------------------------------------------------------------------

project_history.page_title=Delete quality snapshots from project history
project_history.col.year=Year
project_history.col.month=Month
project_history.col.time=Time
project_history.col.events=Events
project_history.col.action=Action
project_history.delete=Delete
project_history.last_snapshot=Last snapshot
project_history.delete_snapshot=Delete snapshot
project_history.snapshot_deleted=The snapshot is deleted.
project_history.are_you_sure_delete_snapshot_x=Are you sure you want to delete the snapshot created on "{0}"?


#------------------------------------------------------------------------------
#
# TIME MACHINE
@@ -623,6 +672,7 @@ quality_profiles.profile_inheritance=Profile inheritance
quality_profiles.available_projects=Available projects
quality_profiles.associated_projects=Associated projects
quality_profiles.no_projects_associated_to_profile_x=No projects are explicitly associated to the profile "{0}".
quality_profiles.projects_warning=List of projects explicitly associated to this Quality profile :
quality_profiles.no_permalinks=No permalinks
quality_profiles.including_x_overriding.suffix=, incl. {0} overriding
quality_profiles.profile_cant_be_edited=This profile can not be edited.
@@ -695,7 +745,7 @@ email_configuration.from_address=From address
email_configuration.from_address.description=Emails will come from this address. For example - "noreply@sonarsource.com". Note that server may ignore this setting (like does GMail).
email_configuration.email_prefix=Email prefix
email_configuration.email_prefix.description=This prefix will be prepended to all outgoing email subjects.
email_configuration.save_settings=Save
email_configuration.save_settings=Save Email Settings
email_configuration.saving_settings=Saving
email_configuration.settings_saved=Settings are saved.

@@ -706,11 +756,28 @@ email_configuration.test.subject=Subject
email_configuration.test.subject_text=Test Message from Sonar
email_configuration.test.message=Message
email_configuration.test.message_text=This is a test message from Sonar
email_configuration.test.send=Send test email
email_configuration.test.sending=Sending test email
email_configuration.test.send=Send Test Email
email_configuration.test.sending=Sending Test Email
email_configuration.test.email_was_sent_to_x=Email was sent to {0}


#------------------------------------------------------------------------------
#
# SERVER KEY CONFIGURATION
#
#------------------------------------------------------------------------------
server_id_configuration.page=Server ID
server_id_configuration.generate_button=Generate ID
server_id_configuration.generating_button=Generating ID...
server_id_configuration.bad_key=The ID is not valid anymore. Please check the organisation and the IP address.
server_id_configuration.information=The Server ID is a unique identifier of this Sonar instance. It is used for example to obtain a license key for the SonarSource's commercial plugins. Two fields have to be provided to generate the ID : organisation name and one of the IP addresses of the machine that hosts this server.
server_id_configuration.organisation.title=Organisation
server_id_configuration.organisation.desc=Name of the organisation
server_id_configuration.ip.title=Fixed IP Address
server_id_configuration.ip.desc=A server ID is linked to the IP address of the hosting machine that runs Sonar. If the server IP address was to changed, the server ID will have to be regenerated. The valid addresses are :
server_id_configuration.generation_error=Organisation and/or IP address are not valid.


#------------------------------------------------------------------------------
#
# NOTIFICATIONS

+ 1
- 1
plugins/sonar-pmd-plugin/pom.xml Ver fichero

@@ -4,7 +4,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>../..</relativePath>
</parent>
<groupId>org.codehaus.sonar.plugins</groupId>

+ 1
- 1
plugins/sonar-squid-java-plugin/pom.xml Ver fichero

@@ -4,7 +4,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>../..</relativePath>
</parent>
<groupId>org.codehaus.sonar.plugins</groupId>

+ 1
- 1
plugins/sonar-squid-java-plugin/src/main/java/org/sonar/java/bytecode/ClassworldsClassLoader.java Ver fichero

@@ -45,7 +45,7 @@ public final class ClassworldsClassLoader {
public static ClassLoader create(Collection<File> bytecodeFilesOrDirectories) {
try {
ClassWorld world = new ClassWorld();
ClassRealm realm = world.newRealm("squid.project");
ClassRealm realm = world.newRealm("squid.project", null /* explicit declaration that parent should be bootstrap class loader */);

for (File bytecode : bytecodeFilesOrDirectories) {
URL url = getURL(bytecode);

+ 16
- 3
plugins/sonar-squid-java-plugin/src/main/java/org/sonar/plugins/squid/SquidPlugin.java Ver fichero

@@ -19,6 +19,7 @@
*/
package org.sonar.plugins.squid;

import org.sonar.api.CoreProperties;
import org.sonar.api.Properties;
import org.sonar.api.Property;
import org.sonar.api.SonarPlugin;
@@ -34,7 +35,9 @@ import java.util.List;
name = "Separate accessors",
description = "Flag whether Squid should separate accessors (getters/setters) from methods. " +
"In that case, accessors are not counted in metrics such as complexity or API documentation.",
project = true, global = true),
project = true,
global = true,
category = CoreProperties.CATEGORY_JAVA),
@Property(key = SquidPluginProperties.FIELDS_TO_EXCLUDE_FROM_LCOM4_COMPUTATION,
defaultValue = SquidPluginProperties.FIELDS_TO_EXCLUDE_FROM_LCOM4_COMPUTATION_DEFAULT_VALUE,
name = "List of fields to exclude from LCOM4 computation",
@@ -42,8 +45,18 @@ import java.util.List;
"unexpectedly and artificially decrease the LCOM4 measure. "
+ "The best example is a logger used by all methods of a class. " +
"All field names to exclude from LCOM4 computation must be separated by a comma.",
project = true, global = true)})
public class SquidPlugin extends SonarPlugin {
project = true,
global = true,
category = CoreProperties.CATEGORY_JAVA),
@Property(
key = CoreProperties.DESIGN_SKIP_DESIGN_PROPERTY,
defaultValue = "" + CoreProperties.DESIGN_SKIP_DESIGN_DEFAULT_VALUE,
name = "Skip design analysis",
project = true,
global = true,
category = CoreProperties.CATEGORY_JAVA)
})
public final class SquidPlugin extends SonarPlugin {

public List getExtensions() {
return Arrays.asList(SquidSensor.class, SquidRuleRepository.class, JavaSourceImporter.class,

+ 20
- 0
plugins/sonar-squid-java-plugin/src/test/java/org/sonar/java/bytecode/ClassworldsClassLoaderTest.java Ver fichero

@@ -19,6 +19,7 @@
*/
package org.sonar.java.bytecode;

import org.codehaus.classworlds.ClassWorld;
import org.junit.Test;
import org.sonar.java.ast.SquidTestUtils;

@@ -39,6 +40,25 @@ public class ClassworldsClassLoaderTest {
assertThat(ClassworldsClassLoader.create(Collections.<File>emptyList()), not(nullValue()));
}

/**
* See SONAR-2824:
* ClassLoader created by {@link ClassworldsClassLoader},
* should be able to load classes only from JDK and from provided list of JAR-files,
* thus it shouldn't be able to load class {@link ClassWorld}.
*/
@Test
public void shouldBeIsolated() throws ClassNotFoundException {
ClassLoader classloader = ClassworldsClassLoader.create(Collections.EMPTY_LIST);
try {
classloader.loadClass(ClassWorld.class.getName());
fail();
} catch (ClassNotFoundException e) {
// ok
}
assertThat(classloader.loadClass("java.lang.Integer"), not(nullValue()));
assertThat(classloader.getResource("java/lang/Integer.class"), not(nullValue()));
}

@Test
public void createFromDirectory() throws ClassNotFoundException {
File dir = SquidTestUtils.getFile("/bytecode/bin/");

+ 1
- 1
plugins/sonar-surefire-plugin/pom.xml Ver fichero

@@ -5,7 +5,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>../..</relativePath>
</parent>


+ 1
- 1
plugins/sonar-surefire-plugin/src/main/java/org/sonar/plugins/surefire/api/AbstractSurefireParser.java Ver fichero

@@ -91,7 +91,7 @@ public abstract class AbstractSurefireParser {
for (String classname : index.getClassnames()) {
if (StringUtils.contains(classname, "$")) {
// Surefire reports classes whereas sonar supports files
String parentClassName = StringUtils.substringBeforeLast(classname, "$");
String parentClassName = StringUtils.substringBefore(classname, "$");
index.merge(classname, parentClassName);
}
}

+ 1
- 1
plugins/sonar-surefire-plugin/src/main/java/org/sonar/plugins/surefire/data/SurefireStaxHandler.java Ver fichero

@@ -125,7 +125,7 @@ public class SurefireStaxHandler implements XmlStreamHandler {
String classname = testCaseCursor.getAttrValue("classname");
String name = testCaseCursor.getAttrValue("name");
if (StringUtils.contains(classname, "$")) {
return StringUtils.substringAfterLast(classname, "$") + "/" + name;
return StringUtils.substringAfter(classname, "$") + "/" + name;
}
return name;
}

+ 13
- 0
plugins/sonar-surefire-plugin/src/test/java/org/sonar/plugins/surefire/api/AbstractSurefireParserTest.java Ver fichero

@@ -111,6 +111,19 @@ public class AbstractSurefireParserTest {
verify(context, never()).saveMeasure(argThat(new IsResource(Scopes.FILE, Qualifiers.FILE, "org.apache.commons.collections.bidimap.AbstractTestBidiMap$TestBidiMapEntrySet")), any(Metric.class), anyDouble());
}

@Test
public void shouldMergeNestedInnerClasses() throws URISyntaxException {
AbstractSurefireParser parser = newParser();

SensorContext context = mockContext();
parser.collect(new Project("foo"), context, getDir("nestedInnerClasses"));

verify(context).saveMeasure(
argThat(new IsResource(Scopes.FILE, Qualifiers.FILE, "org.sonar.plugins.surefire.NestedInnerTest")),
eq(CoreMetrics.TESTS),
eq(3.0));
}


private AbstractSurefireParser newParser() {
return new AbstractSurefireParser() {

+ 69
- 0
plugins/sonar-surefire-plugin/src/test/resources/org/sonar/plugins/surefire/api/AbstractSurefireParserTest/nestedInnerClasses/TEST-org.sonar.plugins.surefire.NestedTest$Inner1$Run.xml Ver fichero

@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" ?>
<testsuite failures="0" time="0.001" errors="0" skipped="0" tests="1" name="org.sonar.plugins.surefire.NestedInnerTest">
<properties>
<property name="java.runtime.name" value="Java(TM) SE Runtime Environment"/>
<property name="sun.boot.library.path" value="/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Libraries"/>
<property name="java.vm.version" value="20.1-b02-384"/>
<property name="awt.nativeDoubleBuffering" value="true"/>
<property name="gopherProxySet" value="false"/>
<property name="mrj.build" value="10M3425"/>
<property name="java.vm.vendor" value="Apple Inc."/>
<property name="java.vendor.url" value="http://www.apple.com/"/>
<property name="path.separator" value=":"/>
<property name="java.vm.name" value="Java HotSpot(TM) 64-Bit Server VM"/>
<property name="file.encoding.pkg" value="sun.io"/>
<property name="user.country" value="US"/>
<property name="sun.java.launcher" value="SUN_STANDARD"/>
<property name="sun.os.patch.level" value="unknown"/>
<property name="java.vm.specification.name" value="Java Virtual Machine Specification"/>
<property name="user.dir" value="/Users/sbrandhof/projects/github/sonar/plugins/sonar-surefire-plugin"/>
<property name="java.runtime.version" value="1.6.0_26-b03-384-10M3425"/>
<property name="java.awt.graphicsenv" value="apple.awt.CGraphicsEnvironment"/>
<property name="java.endorsed.dirs" value="/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/lib/endorsed"/>
<property name="os.arch" value="x86_64"/>
<property name="java.io.tmpdir" value="/var/folders/H1/H1p3aGauF5myqknnfgovp++++TI/-Tmp-/"/>
<property name="line.separator" value="
"/>
<property name="java.vm.specification.vendor" value="Sun Microsystems Inc."/>
<property name="os.name" value="Mac OS X"/>
<property name="classworlds.conf" value="/Users/sbrandhof/Dropbox/dev/softwares/apache-maven-2.2.1/bin/m2.conf"/>
<property name="sun.jnu.encoding" value="MacRoman"/>
<property name="java.library.path" value=".:/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java"/>
<property name="java.specification.name" value="Java Platform API Specification"/>
<property name="java.class.version" value="50.0"/>
<property name="sun.management.compiler" value="HotSpot 64-Bit Tiered Compilers"/>
<property name="os.version" value="10.6.8"/>
<property name="http.nonProxyHosts" value="local|*.local|169.254/16|*.169.254/16"/>
<property name="user.home" value="/Users/sbrandhof"/>
<property name="user.timezone" value="Europe/Paris"/>
<property name="java.awt.printerjob" value="apple.awt.CPrinterJob"/>
<property name="java.specification.version" value="1.6"/>
<property name="file.encoding" value="MacRoman"/>
<property name="user.name" value="sbrandhof"/>
<property name="java.class.path" value="/Users/sbrandhof/Dropbox/dev/softwares/apache-maven-2.2.1/boot/classworlds-1.1.jar"/>
<property name="java.vm.specification.version" value="1.0"/>
<property name="sun.arch.data.model" value="64"/>
<property name="java.home" value="/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home"/>
<property name="sun.java.command" value="org.codehaus.classworlds.Launcher &quot;clean&quot; &quot;install&quot;"/>
<property name="java.specification.vendor" value="Sun Microsystems Inc."/>
<property name="user.language" value="en"/>
<property name="awt.toolkit" value="apple.awt.CToolkit"/>
<property name="java.vm.info" value="mixed mode"/>
<property name="java.version" value="1.6.0_26"/>
<property name="java.ext.dirs" value="/Library/Java/Extensions:/System/Library/Java/Extensions:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/lib/ext"/>
<property name="sun.boot.class.path" value="/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/jsfd.jar:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/classes.jar:/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaRuntimeSupport.framework/Resources/Java/JavaRuntimeSupport.jar:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/ui.jar:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/laf.jar:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/sunrsasign.jar:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/jsse.jar:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/jce.jar:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/charsets.jar"/>
<property name="java.vendor" value="Apple Inc."/>
<property name="maven.home" value="/Users/sbrandhof/Dropbox/dev/softwares/apache-maven-2.2.1"/>
<property name="file.separator" value="/"/>
<property name="java.vendor.url.bug" value="http://bugreport.apple.com/"/>
<property name="sun.cpu.endian" value="little"/>
<property name="sun.io.unicode.encoding" value="UnicodeLittle"/>
<property name="mrj.version" value="1060.1.6.0_26-384"/>
<property name="socksNonProxyHosts" value="local|*.local|169.254/16|*.169.254/16"/>
<property name="ftp.nonProxyHosts" value="local|*.local|169.254/16|*.169.254/16"/>
<property name="sun.cpu.isalist" value=""/>
</properties>
<testcase time="0" classname="org.sonar.plugins.surefire.NestedInnerTest$Inner1$Run" name="test1"/>
<testcase time="0" classname="org.sonar.plugins.surefire.NestedInnerTest$Inner2$Run" name="test2"/>
<testcase time="0" classname="org.sonar.plugins.surefire.NestedInnerTest$Inner3$Run" name="test3"/>
</testsuite>

+ 67
- 0
plugins/sonar-surefire-plugin/src/test/resources/org/sonar/plugins/surefire/api/AbstractSurefireParserTest/nestedInnerClasses/TEST-org.sonar.plugins.surefire.NestedTest$Inner2$Run.xml Ver fichero

@@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8" ?>
<testsuite failures="0" time="0.023" errors="0" skipped="0" tests="1" name="org.sonar.plugins.surefire.NestedInnerTest$Inner2$Run">
<properties>
<property name="java.runtime.name" value="Java(TM) SE Runtime Environment"/>
<property name="sun.boot.library.path" value="/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Libraries"/>
<property name="java.vm.version" value="20.1-b02-384"/>
<property name="awt.nativeDoubleBuffering" value="true"/>
<property name="gopherProxySet" value="false"/>
<property name="mrj.build" value="10M3425"/>
<property name="java.vm.vendor" value="Apple Inc."/>
<property name="java.vendor.url" value="http://www.apple.com/"/>
<property name="path.separator" value=":"/>
<property name="java.vm.name" value="Java HotSpot(TM) 64-Bit Server VM"/>
<property name="file.encoding.pkg" value="sun.io"/>
<property name="user.country" value="US"/>
<property name="sun.java.launcher" value="SUN_STANDARD"/>
<property name="sun.os.patch.level" value="unknown"/>
<property name="java.vm.specification.name" value="Java Virtual Machine Specification"/>
<property name="user.dir" value="/Users/sbrandhof/projects/github/sonar/plugins/sonar-surefire-plugin"/>
<property name="java.runtime.version" value="1.6.0_26-b03-384-10M3425"/>
<property name="java.awt.graphicsenv" value="apple.awt.CGraphicsEnvironment"/>
<property name="java.endorsed.dirs" value="/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/lib/endorsed"/>
<property name="os.arch" value="x86_64"/>
<property name="java.io.tmpdir" value="/var/folders/H1/H1p3aGauF5myqknnfgovp++++TI/-Tmp-/"/>
<property name="line.separator" value="
"/>
<property name="java.vm.specification.vendor" value="Sun Microsystems Inc."/>
<property name="os.name" value="Mac OS X"/>
<property name="classworlds.conf" value="/Users/sbrandhof/Dropbox/dev/softwares/apache-maven-2.2.1/bin/m2.conf"/>
<property name="sun.jnu.encoding" value="MacRoman"/>
<property name="java.library.path" value=".:/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java"/>
<property name="java.specification.name" value="Java Platform API Specification"/>
<property name="java.class.version" value="50.0"/>
<property name="sun.management.compiler" value="HotSpot 64-Bit Tiered Compilers"/>
<property name="os.version" value="10.6.8"/>
<property name="http.nonProxyHosts" value="local|*.local|169.254/16|*.169.254/16"/>
<property name="user.home" value="/Users/sbrandhof"/>
<property name="user.timezone" value="Europe/Paris"/>
<property name="java.awt.printerjob" value="apple.awt.CPrinterJob"/>
<property name="java.specification.version" value="1.6"/>
<property name="file.encoding" value="MacRoman"/>
<property name="user.name" value="sbrandhof"/>
<property name="java.class.path" value="/Users/sbrandhof/Dropbox/dev/softwares/apache-maven-2.2.1/boot/classworlds-1.1.jar"/>
<property name="java.vm.specification.version" value="1.0"/>
<property name="sun.arch.data.model" value="64"/>
<property name="java.home" value="/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home"/>
<property name="sun.java.command" value="org.codehaus.classworlds.Launcher &quot;clean&quot; &quot;install&quot;"/>
<property name="java.specification.vendor" value="Sun Microsystems Inc."/>
<property name="user.language" value="en"/>
<property name="awt.toolkit" value="apple.awt.CToolkit"/>
<property name="java.vm.info" value="mixed mode"/>
<property name="java.version" value="1.6.0_26"/>
<property name="java.ext.dirs" value="/Library/Java/Extensions:/System/Library/Java/Extensions:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/lib/ext"/>
<property name="sun.boot.class.path" value="/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/jsfd.jar:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/classes.jar:/System/Library/Frameworks/JavaVM.framework/Frameworks/JavaRuntimeSupport.framework/Resources/Java/JavaRuntimeSupport.jar:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/ui.jar:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/laf.jar:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/sunrsasign.jar:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/jsse.jar:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/jce.jar:/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Classes/charsets.jar"/>
<property name="java.vendor" value="Apple Inc."/>
<property name="maven.home" value="/Users/sbrandhof/Dropbox/dev/softwares/apache-maven-2.2.1"/>
<property name="file.separator" value="/"/>
<property name="java.vendor.url.bug" value="http://bugreport.apple.com/"/>
<property name="sun.cpu.endian" value="little"/>
<property name="sun.io.unicode.encoding" value="UnicodeLittle"/>
<property name="mrj.version" value="1060.1.6.0_26-384"/>
<property name="socksNonProxyHosts" value="local|*.local|169.254/16|*.169.254/16"/>
<property name="ftp.nonProxyHosts" value="local|*.local|169.254/16|*.169.254/16"/>
<property name="sun.cpu.isalist" value=""/>
</properties>
<testcase time="0" classname="org.sonar.plugins.surefire.NestedInnerTest$Inner2$Run" name="test2"/>
</testsuite>

+ 47
- 13
pom.xml Ver fichero

@@ -5,7 +5,7 @@
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<packaging>pom</packaging>
<version>2.10</version>
<version>2.11</version>
<name>Sonar</name>
<url>http://www.sonarsource.org</url>
<description>Put technical debt under control</description>
@@ -209,6 +209,10 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.8</version>
<configuration>
<author>false</author>
<linksource>true</linksource>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -272,6 +276,7 @@
<version>1.1</version>
<extensions>true</extensions>
</plugin>

</plugins>
</pluginManagement>

@@ -388,17 +393,6 @@
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<excludePackageNames>
net.*
</excludePackageNames>
<author>false</author>
<linksource>true</linksource>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jxr-plugin</artifactId>
@@ -469,6 +463,26 @@
<runOrder>random</runOrder>
</configuration>
</plugin>
<plugin>
<!-- this plugin is used to list the licenses of all the dependencies :
mvn org.codehaus.mojo:license-maven-plugin:aggregate-add-third-party
-->
<groupId>org.codehaus.mojo</groupId>
<artifactId>license-maven-plugin</artifactId>
<version>1.0-beta-3</version>
<configuration>
<groupByLicense>true</groupByLicense>
<licenseMerges>
<licenseMerge>The Apache Software License, Version 2.0|Apache License 2.0|Apache 2|Apache License Version 2|Apache License Version 2.0|Apache Public License 2.0|Apache Software License - Version 2.0|Apache Software Licenses</licenseMerge>
<licenseMerge>GNU Lesser General Public License, Version 2.1|GNU Lesser General Public License Version 2.1|GNU Lesser General Public License (LGPL), Version 2.1</licenseMerge>
<licenseMerge>BSD|BSD License|The BSD License</licenseMerge>
<licenseMerge>Common Public License, Version 1.0|Common Public License - v 1.0|Common Public License Version 1.0</licenseMerge>
<licenseMerge>Eclipse Public License, Version 1.0|Eclipse Public License - Version 1.0</licenseMerge>
<licenseMerge>Common Development and Distribution License, Version 1.0|COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0|Common Development and Distribution License (CDDL) v1.0</licenseMerge>
<licenseMerge>MIT License|The MIT License</licenseMerge>
</licenseMerges>
</configuration>
</plugin>
</plugins>
</build>

@@ -863,7 +877,7 @@
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby-complete</artifactId>
<version>1.6.1</version>
<version>1.6.4</version>
</dependency>
<dependency>
<groupId>geronimo-spec</groupId>
@@ -1083,6 +1097,26 @@
</plugins>
</build>
</profile>
<profile>
<id>javadoc</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<configuration>
<excludePackageNames>
net.*:org.sonar.application:org.sonar.server:org.sonar.graph:org.sonar.batch:org.sonar.channel:org.sonar.java:org.sonar.maven*:org.sonar.plugins.*:org.sonar.colorizer:org.sonar.squid:org.sonar.core:org.sonar.jpa:org.sonar.duplications:org.sonar.markdown:com.*
</excludePackageNames>
<author>false</author>
<linksource>true</linksource>
<reportOutputDirectory>${project.reporting.outputDirectory}/${project.version}/apidocs</reportOutputDirectory>
</configuration>
</plugin>
</plugins>
</build>
</profile>

<profile>
<id>sanity-checks</id>

+ 1
- 1
samples/pom.xml Ver fichero

@@ -5,7 +5,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>..</relativePath>
</parent>
<groupId>org.codehaus.sonar.samples</groupId>

+ 1
- 1
sonar-application/pom.xml Ver fichero

@@ -4,7 +4,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
</parent>
<artifactId>sonar-application</artifactId>
<packaging>jar</packaging>

+ 17
- 21
sonar-application/src/main/assembly/conf/sonar.properties Ver fichero

@@ -25,15 +25,14 @@
#sonar.ajp13.port: 8009


#---------------------------------------------------------
#-----------------------------------------------------------------------
# DATABASE
#
# IMPORTANT : the embedded database Derby is used by default.
# It is recommended for tests only. Please use an other database
# for production environment (MySQL, Oracle, Postgresql,
# SQLServer)
# It is recommended for tests only. Please use an external database
# for production environment (MySQL, Oracle, Postgresql, SQLServer)
#
#---------------------------------------------------------
#-----------------------------------------------------------------------

#----- Credentials
# Permissions to create tables and indexes must be granted to JDBC user.
@@ -66,15 +65,20 @@ sonar.jdbc.driverClassName: org.apache.derby.jdbc.ClientDriver


#----- Oracle 10g/11g
# To use Oracle database :
# - Copy the JDBC driver to extensions/jdbc-driver/oracle.
# To connect to Oracle database :
#
# - It's recommended to use the latest version of the JDBC driver (either ojdbc6.jar for Java 6 or ojdbc5.jar for Java 5).
# Download it in http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html
# - Copy the driver to the directory extensions/jdbc-driver/oracle/
# - Comment the embedded database and uncomment the following properties. The validation query is optional.
#
#sonar.jdbc.url: jdbc:oracle:thin:@localhost/XE
#sonar.jdbc.driverClassName: oracle.jdbc.driver.OracleDriver
#sonar.jdbc.validationQuery: select 1 from dual

# Activate if more than one Sonar Oracle schemas on the data server (for example different versions installed).
# In that case, use the same property during maven analysis (-Dsonar.hibernate.default_schema=xxx)
# Uncomment the following property if multiple Sonar schemas are installed on the same server
# (for example with different versions).
# In that case, use the same property during project analysis (-Dsonar.hibernate.default_schema=<schema>)
#sonar.hibernate.default_schema: sonar

#----- PostgreSQL 8.x, 9.x
@@ -83,6 +87,9 @@ sonar.jdbc.driverClassName: org.apache.derby.jdbc.ClientDriver
#sonar.jdbc.driverClassName: org.postgresql.Driver
#sonar.jdbc.validationQuery: select 1

# If the PostgreSql database contains several schemas, the following property must define the schema to be used
#sonar.jdbc.postgreSearchPath: public


#----- Microsoft SQLServer
# The Jtds open source driver is available in extensions/jdbc-driver/mssql. More details on http://jtds.sourceforge.net
@@ -93,7 +100,7 @@ sonar.jdbc.driverClassName: org.apache.derby.jdbc.ClientDriver


#----- Connection pool settings
sonar.jdbc.maxActive: 10
sonar.jdbc.maxActive: 20
sonar.jdbc.maxIdle: 5
sonar.jdbc.minIdle: 2
sonar.jdbc.maxWait: 5000
@@ -101,17 +108,6 @@ sonar.jdbc.minEvictableIdleTimeMillis: 600000
sonar.jdbc.timeBetweenEvictionRunsMillis: 30000


#----- JDBC Datasource bounded to JNDI
# When sonar webapp is deployed into a JEE server, the JDBC datasource can be configured into the JEE server and registered into JNDI.
# In such a case Sonar uses this datasource to connect to database.
# If you activate this feature, then the properties starting with "sonar.jdbc." can be commented, except "sonar.jdbc.dialect".
# The JDBC driver must still be deployed into the directory /extensions/jdbc-driver.
#sonar.jdbc.jndiName: jdbc/sonar

# Values are : mysql, mssql, derby, oracle, postgresql
#sonar.jdbc.dialect=


#---------------------------------------------------------
# UPDATE CENTER
#---------------------------------------------------------

+ 1
- 1
sonar-batch-bootstrapper/pom.xml Ver fichero

@@ -5,7 +5,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
</parent>

<artifactId>sonar-batch-bootstrapper</artifactId>

+ 1
- 1
sonar-batch-maven-compat/pom.xml Ver fichero

@@ -5,7 +5,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
</parent>

<artifactId>sonar-batch-maven-compat</artifactId>

+ 1
- 1
sonar-batch/pom.xml Ver fichero

@@ -4,7 +4,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
</parent>

<groupId>org.codehaus.sonar</groupId>

+ 8
- 7
sonar-batch/src/main/java/org/sonar/batch/AbstractMavenPluginExecutor.java Ver fichero

@@ -35,17 +35,14 @@ public abstract class AbstractMavenPluginExecutor implements MavenPluginExecutor
public final MavenPluginHandler execute(Project project, ProjectDefinition projectDefinition, MavenPluginHandler handler) {
for (String goal : handler.getGoals()) {
MavenPlugin plugin = MavenPlugin.getPlugin(project.getPom(), handler.getGroupId(), handler.getArtifactId());
execute(project, getGoal(handler.getGroupId(), handler.getArtifactId(), (plugin!=null && plugin.getPlugin()!=null ? plugin.getPlugin().getVersion() : null), goal));
execute(project,
projectDefinition,
getGoal(handler.getGroupId(), handler.getArtifactId(), (plugin != null && plugin.getPlugin() != null ? plugin.getPlugin().getVersion() : null), goal));
}

if (project.getPom()!=null) {
MavenProjectConverter.synchronizeFileSystem(project.getPom(), projectDefinition);
}

return handler;
}

public final void execute(Project project, String goal) {
public final void execute(Project project, ProjectDefinition projectDefinition, String goal) {
TimeProfiler profiler = new TimeProfiler().start("Execute " + goal);
ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
try {
@@ -57,6 +54,10 @@ public abstract class AbstractMavenPluginExecutor implements MavenPluginExecutor
Thread.currentThread().setContextClassLoader(currentClassLoader);
profiler.stop();
}

if (project.getPom() != null) {
MavenProjectConverter.synchronizeFileSystem(project.getPom(), projectDefinition);
}
}

public abstract void concreteExecute(MavenProject pom, String goal) throws Exception;

+ 1
- 1
sonar-batch/src/main/java/org/sonar/batch/FakeMavenPluginExecutor.java Ver fichero

@@ -24,7 +24,7 @@ import org.sonar.api.batch.maven.MavenPluginHandler;
import org.sonar.api.resources.Project;

public final class FakeMavenPluginExecutor implements MavenPluginExecutor {
public void execute(Project project, String goal) {
public void execute(Project project, ProjectDefinition projectDef, String goal) {
// do nothing
}


+ 1
- 1
sonar-batch/src/main/java/org/sonar/batch/MavenPluginExecutor.java Ver fichero

@@ -26,7 +26,7 @@ import org.sonar.api.resources.Project;

public interface MavenPluginExecutor extends BatchComponent {

void execute(Project project, String goal);
void execute(Project project, ProjectDefinition def, String goal);

MavenPluginHandler execute(Project project, ProjectDefinition def, MavenPluginHandler handler);


+ 20
- 24
sonar-batch/src/main/java/org/sonar/batch/ServerMetadata.java Ver fichero

@@ -31,43 +31,39 @@ import java.util.Date;

public class ServerMetadata extends Server {

private String id;
private String version;
private String url;
private Date startTime;
private Configuration conf;

public ServerMetadata(Configuration conf) {
id = conf.getString(CoreProperties.SERVER_ID);
version = conf.getString(CoreProperties.SERVER_VERSION);
url = getURL(conf);
String dateString = conf.getString(CoreProperties.SERVER_STARTTIME);
if (dateString!=null) {
try {
this.startTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(dateString);

} catch (ParseException e) {
LoggerFactory.getLogger(getClass()).error("The property " + CoreProperties.SERVER_STARTTIME + " is badly formatted.", e);
}
}
}

public static String getURL(Configuration conf) {
return StringUtils.removeEnd(conf.getString("sonar.host.url", "http://localhost:9000"), "/");
this.conf = conf;
}

public String getId() {
return id;
return conf.getString(CoreProperties.SERVER_ID);
}

public String getVersion() {
return version;
return conf.getString(CoreProperties.SERVER_VERSION);
}

public Date getStartedAt() {
return startTime;
String dateString = conf.getString(CoreProperties.SERVER_STARTTIME);
if (dateString != null) {
try {
return new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(dateString);

} catch (ParseException e) {
LoggerFactory.getLogger(getClass()).error("The property " + CoreProperties.SERVER_STARTTIME + " is badly formatted.", e);
}
}
return null;
}

public String getURL() {
return url;
return StringUtils.removeEnd(conf.getString("sonar.host.url", "http://localhost:9000"), "/");
}

@Override
public String getPermanentServerId() {
return conf.getString(CoreProperties.PERMANENT_SERVER_ID);
}
}

+ 7
- 5
sonar-batch/src/main/java/org/sonar/batch/bootstrap/BatchExtensionInstaller.java Ver fichero

@@ -64,12 +64,14 @@ public final class BatchExtensionInstaller implements BatchComponent {
List<ExtensionProvider> providers = module.getComponents(ExtensionProvider.class);
for (ExtensionProvider provider : providers) {
Object obj = provider.provide();
if (obj instanceof Iterable) {
for (Object extension : (Iterable) obj) {
installExtension(module, extension);
if (obj != null) {
if (obj instanceof Iterable) {
for (Object extension : (Iterable) obj) {
installExtension(module, extension);
}
} else {
installExtension(module, obj);
}
} else {
installExtension(module, obj);
}
}
}

+ 1
- 1
sonar-batch/src/main/java/org/sonar/batch/index/EventPersister.java Ver fichero

@@ -46,7 +46,7 @@ public final class EventPersister {
}

public void deleteEvent(Event event) {
session.remove(event);
session.removeWithoutFlush(event);
session.commit();
}


+ 5
- 2
sonar-batch/src/main/java/org/sonar/batch/phases/MavenPhaseExecutor.java Ver fichero

@@ -21,6 +21,7 @@ package org.sonar.batch.phases;

import org.apache.commons.lang.StringUtils;
import org.sonar.api.BatchComponent;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.resources.Project;
import org.sonar.batch.MavenPluginExecutor;

@@ -29,15 +30,17 @@ public class MavenPhaseExecutor implements BatchComponent {
public static final String PROP_PHASE = "sonar.phase";

private MavenPluginExecutor executor;
private ProjectDefinition projectDef;

public MavenPhaseExecutor(MavenPluginExecutor executor) {
public MavenPhaseExecutor(ProjectDefinition projectDef, MavenPluginExecutor executor) {
this.projectDef = projectDef;
this.executor = executor;
}

public void execute(Project project) {
String mavenPhase = (String) project.getProperty(PROP_PHASE);
if (!StringUtils.isBlank(mavenPhase)) {
executor.execute(project, mavenPhase);
executor.execute(project, projectDef, mavenPhase);
}
}
}

+ 4
- 4
sonar-batch/src/test/java/org/sonar/batch/bootstrap/BootstrapModuleTest.java Ver fichero

@@ -19,6 +19,9 @@
*/
package org.sonar.batch.bootstrap;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

import org.junit.Test;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.bootstrap.ProjectReactor;
@@ -26,13 +29,10 @@ import org.sonar.api.batch.maven.MavenPluginHandler;
import org.sonar.api.resources.Project;
import org.sonar.batch.MavenPluginExecutor;

import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

public class BootstrapModuleTest {

class MyMavenPluginExecutor implements MavenPluginExecutor {
public void execute(Project project, String goal) {
public void execute(Project project, ProjectDefinition projectDef, String goal) {
}

public MavenPluginHandler execute(Project project, ProjectDefinition projectDef, MavenPluginHandler handler) {

+ 13
- 7
sonar-batch/src/test/java/org/sonar/batch/phases/MavenPhaseExecutorTest.java Ver fichero

@@ -19,32 +19,38 @@
*/
package org.sonar.batch.phases;

import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

import org.apache.commons.configuration.PropertiesConfiguration;
import org.junit.Test;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.resources.Project;
import org.sonar.batch.MavenPluginExecutor;

import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;

public class MavenPhaseExecutorTest {

@Test
public void doNothingIfNoPhase() {
ProjectDefinition projectDef = ProjectDefinition.create();
MavenPluginExecutor mavenPluginExecutor = mock(MavenPluginExecutor.class);
MavenPhaseExecutor phaseExecutor = new MavenPhaseExecutor(mavenPluginExecutor);
MavenPhaseExecutor phaseExecutor = new MavenPhaseExecutor(projectDef, mavenPluginExecutor);


Project project = new Project("key");
phaseExecutor.execute(project);

verify(mavenPluginExecutor, never()).execute(eq(project), anyString());
verify(mavenPluginExecutor, never()).execute(eq(project), eq(projectDef), anyString());
}

@Test
public void executePhase() {
ProjectDefinition projectDef = ProjectDefinition.create();
MavenPluginExecutor mavenPluginExecutor = mock(MavenPluginExecutor.class);
MavenPhaseExecutor phaseExecutor = new MavenPhaseExecutor(mavenPluginExecutor);
MavenPhaseExecutor phaseExecutor = new MavenPhaseExecutor(projectDef, mavenPluginExecutor);

Project project = new Project("key");
PropertiesConfiguration conf = new PropertiesConfiguration();
@@ -53,6 +59,6 @@ public class MavenPhaseExecutorTest {

phaseExecutor.execute(project);

verify(mavenPluginExecutor).execute(project, "myphase");
verify(mavenPluginExecutor).execute(project, projectDef, "myphase");
}
}

+ 1
- 1
sonar-channel/pom.xml Ver fichero

@@ -4,7 +4,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>..</relativePath>
</parent>
<artifactId>sonar-channel</artifactId>

+ 1
- 1
sonar-check-api/pom.xml Ver fichero

@@ -4,7 +4,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>..</relativePath>
</parent>
<artifactId>sonar-check-api</artifactId>

+ 1
- 1
sonar-colorizer/pom.xml Ver fichero

@@ -4,7 +4,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
<relativePath>..</relativePath>
</parent>
<artifactId>sonar-colorizer</artifactId>

+ 1
- 35
sonar-core-maven-plugin/pom.xml Ver fichero

@@ -4,7 +4,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
</parent>

<!-- do not change, it's used when deploying in sonar internal repository -->
@@ -36,29 +36,6 @@

-->

<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-dependency-tree</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-plugin-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-batch</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
@@ -77,17 +54,6 @@
<version>2.0.7</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar-plugin-api</artifactId>
<version>${project.version}</version>
<scope>test</scope>
<type>test-jar</type>
</dependency>
</dependencies>
<build>
<plugins>

+ 2
- 153
sonar-core-maven-plugin/src/main/java/org/sonar/maven2/BatchMojo.java Ver fichero

@@ -19,33 +19,9 @@
*/
package org.sonar.maven2;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import org.apache.commons.configuration.*;
import org.apache.commons.io.IOUtils;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactCollector;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.RuntimeInformation;
import org.apache.maven.lifecycle.LifecycleExecutor;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.PluginManager;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectBuilder;
import org.apache.maven.shared.dependency.tree.DependencyTreeBuilder;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.batch.Batch;
import org.sonar.batch.MavenProjectConverter;
import org.sonar.batch.bootstrapper.EnvironmentInformation;

import java.io.InputStream;

/**
* @goal internal
@@ -54,135 +30,8 @@ import java.io.InputStream;
*/
public final class BatchMojo extends AbstractMojo {

/**
* @parameter expression="${session}"
* @required
* @readonly
*/
private MavenSession session;

/**
* @parameter expression="${project}"
* @required
* @readonly
*/
private MavenProject project;

/**
* @component
* @required
*/
private LifecycleExecutor lifecycleExecutor;

/**
* @component
* @required
*/
private PluginManager pluginManager;

/**
* The artifact factory to use.
*
* @component
* @required
* @readonly
*/
private ArtifactFactory artifactFactory;

/**
* The artifact repository to use.
*
* @parameter expression="${localRepository}"
* @required
* @readonly
*/
private ArtifactRepository localRepository;

/**
* The artifact metadata source to use.
*
* @component
* @required
* @readonly
*/
private ArtifactMetadataSource artifactMetadataSource;

/**
* The artifact collector to use.
*
* @component
* @required
* @readonly
*/
private ArtifactCollector artifactCollector;

/**
* The dependency tree builder to use.
*
* @component
* @required
* @readonly
*/
private DependencyTreeBuilder dependencyTreeBuilder;

/**
* @component
* @required
* @readonly
*/
private MavenProjectBuilder projectBuilder;

/**
* @component
* @required
* @readonly
*/
private RuntimeInformation runtimeInformation;

public void execute() throws MojoExecutionException, MojoFailureException {
initLogging();
executeBatch();
}

private void executeBatch() throws MojoExecutionException {
ProjectDefinition def = MavenProjectConverter.convert(session.getSortedProjects(), project);
ProjectReactor reactor = new ProjectReactor(def);

Batch batch = Batch.create(reactor, getInitialConfiguration(), session, project,
getLog(), lifecycleExecutor, pluginManager, artifactFactory,
localRepository, artifactMetadataSource, artifactCollector,
dependencyTreeBuilder, projectBuilder, getEnvironmentInformation(), Maven2PluginExecutor.class);
batch.execute();
}

private EnvironmentInformation getEnvironmentInformation() {
String mavenVersion = runtimeInformation.getApplicationVersion().toString();
return new EnvironmentInformation("Maven", mavenVersion);
}

private Configuration getInitialConfiguration() {
CompositeConfiguration configuration = new CompositeConfiguration();
configuration.addConfiguration(new SystemConfiguration());
configuration.addConfiguration(new EnvironmentConfiguration());
configuration.addConfiguration(new MapConfiguration(project.getModel().getProperties()));
return configuration;
}

private void initLogging() throws MojoExecutionException {
LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
JoranConfigurator jc = new JoranConfigurator();
jc.setContext(context);
context.reset();
InputStream input = getClass().getResourceAsStream("/org/sonar/batch/logback.xml");
System.setProperty("ROOT_LOGGER_LEVEL", getLog().isDebugEnabled() ? "DEBUG" : "INFO");
try {
jc.doConfigure(input);

} catch (JoranException e) {
throw new MojoExecutionException("can not initialize logging", e);

} finally {
IOUtils.closeQuietly(input);
}
throw new MojoExecutionException("The version 1.0-beta-1 of org.codehaus.mojo:sonar-maven-plugin is not supported anymore. "
+ "Please upgrade to 1.0-beta-2 for Maven2 or 2.0-beta-2 for Maven3.");
}
}

+ 0
- 55
sonar-core-maven-plugin/src/main/java/org/sonar/maven2/Maven2PluginExecutor.java Ver fichero

@@ -1,55 +0,0 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2011 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.maven2;

import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.ReactorManager;
import org.apache.maven.lifecycle.LifecycleExecutor;
import org.apache.maven.project.MavenProject;
import org.sonar.batch.AbstractMavenPluginExecutor;

import java.util.Arrays;

public class Maven2PluginExecutor extends AbstractMavenPluginExecutor {

private LifecycleExecutor lifecycleExecutor;
private MavenSession mavenSession;

public Maven2PluginExecutor(LifecycleExecutor le, MavenSession mavenSession) {
this.lifecycleExecutor = le;
this.mavenSession = mavenSession;
}

@Override
public void concreteExecute(MavenProject pom, String goal) throws Exception {
ReactorManager reactor = new ReactorManager(Arrays.asList(pom));
MavenSession clonedSession = new MavenSession(mavenSession.getContainer(),
mavenSession.getSettings(),
mavenSession.getLocalRepository(),
mavenSession.getEventDispatcher(),
reactor,
Arrays.asList(goal),
mavenSession.getExecutionRootDirectory(),
mavenSession.getExecutionProperties(),
mavenSession.getStartTime());
lifecycleExecutor.execute(clonedSession, reactor, clonedSession.getEventDispatcher());
}

}

+ 0
- 23
sonar-core-maven-plugin/src/test/resources/logback-text.xml Ver fichero

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8" ?>

<configuration>

<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>
%d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n
</pattern>
</layout>
</appender>

<logger name="org.sonar.DBSTATISTICS">
<level value="INFO"/>
</logger>

<root>
<level value="WARN"/>
<appender-ref ref="STDOUT"/>
</root>

</configuration>

+ 1
- 1
sonar-core/pom.xml Ver fichero

@@ -4,7 +4,7 @@
<parent>
<groupId>org.codehaus.sonar</groupId>
<artifactId>sonar</artifactId>
<version>2.10</version>
<version>2.11</version>
</parent>
<artifactId>sonar-core</artifactId>
<name>Sonar :: Core</name>

+ 5
- 0
sonar-core/src/main/java/org/sonar/api/database/configuration/DatabaseConfiguration.java Ver fichero

@@ -48,6 +48,11 @@ public class DatabaseConfiguration extends BaseConfiguration {
public void load() {
clear();

// Ugly workaround before the move to myBatis
// Session is not up-to-date when Ruby on Rails inserts new rows in its own transaction. Seems like
// Hibernate keeps a cache...
getSession().commit();

List<Property> properties = getSession()
.createQuery("from " + Property.class.getSimpleName() + " p where p.resourceId is null and p.userId is null")
.getResultList();

+ 2
- 7
sonar-core/src/main/java/org/sonar/api/database/configuration/Property.java Ver fichero

@@ -19,7 +19,7 @@
*/
package org.sonar.api.database.configuration;

import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.sonar.api.database.BaseIdentifiable;

@@ -139,11 +139,6 @@ public class Property extends BaseIdentifiable {

@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.append("key", key)
.append("resource", resourceId)
.append("user", userId)
.append("value", value)
.toString();
return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString();
}
}

+ 18
- 21
sonar-core/src/main/java/org/sonar/core/components/CacheRuleFinder.java Ver fichero

@@ -23,6 +23,7 @@ import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import com.google.common.collect.Maps;
import org.sonar.api.rules.Rule;
import org.sonar.api.rules.RuleQuery;
import org.sonar.jpa.session.DatabaseSessionFactory;

import java.util.Map;
@@ -39,36 +40,32 @@ public final class CacheRuleFinder extends DefaultRuleFinder {
@Override
public Rule findById(int ruleId) {
Rule rule = rulesById.get(ruleId);
if (rule==null) {
if (rule == null) {
rule = doFindById(ruleId);
addToCache(rule);
if (rule != null) {
loadRepository(rule.getRepositoryKey());
}
}
return rule;
}

@Override
public Rule findByKey(String repositoryKey, String key) {
Map<String,Rule> repoRules = rulesByKey.get(repositoryKey);
Rule rule = null;
if (repoRules!=null) {
rule = repoRules.get(key);
}
if (rule == null) {
rule = doFindByKey(repositoryKey, key);
addToCache(rule);
}
return rule;
public Rule findByKey(String repositoryKey, String ruleKey) {
Map<String, Rule> repository = loadRepository(repositoryKey);
return repository.get(ruleKey);
}

private void addToCache(Rule rule) {
if (rule != null) {
rulesById.put(rule.getId(), rule);
Map<String, Rule> repoRules = rulesByKey.get(rule.getKey());
if (repoRules==null) {
repoRules = Maps.newHashMap();
rulesByKey.put(rule.getRepositoryKey(), repoRules);
private Map<String, Rule> loadRepository(String repositoryKey) {
Map<String, Rule> repository = rulesByKey.get(repositoryKey);
if (repository == null) {
repository = Maps.newHashMap();
rulesByKey.put(repositoryKey, repository);

for (Rule rule : findAll(RuleQuery.create().withRepositoryKey(repositoryKey))) {
repository.put(rule.getKey(), rule);
rulesById.put(rule.getId(), rule);
}
repoRules.put(rule.getKey(), rule);
}
return repository;
}
}

+ 1
- 1
sonar-core/src/main/java/org/sonar/core/plugins/DefaultPluginMetadata.java Ver fichero

@@ -28,7 +28,7 @@ import org.sonar.api.platform.PluginMetadata;
import java.io.File;
import java.util.List;

public final class DefaultPluginMetadata implements PluginMetadata, Comparable<PluginMetadata> {
public class DefaultPluginMetadata implements PluginMetadata, Comparable<PluginMetadata> {
private File file;
private List<File> deployedFiles = Lists.newArrayList();
private List<File> deprecatedExtensions = Lists.newArrayList();

+ 4
- 0
sonar-core/src/main/java/org/sonar/jpa/dialect/Derby.java Ver fichero

@@ -39,6 +39,10 @@ public class Derby implements Dialect {
return "derby";
}

public String getActiveRecordJdbcAdapter() {
return "jdbc";
}

public Class<? extends org.hibernate.dialect.Dialect> getHibernateDialectClass() {
return DerbyWithDecimalDialect.class;
}

+ 5
- 0
sonar-core/src/main/java/org/sonar/jpa/dialect/Dialect.java Ver fichero

@@ -39,6 +39,11 @@ public interface Dialect {
*/
String getActiveRecordDialectCode();

/**
* @return the activerecord-jdbc adapter. See the property 'adapter' in database.yml
*/
String getActiveRecordJdbcAdapter();

/**
* Used to autodetect a dialect for a given driver URL
*

+ 4
- 0
sonar-core/src/main/java/org/sonar/jpa/dialect/HsqlDb.java Ver fichero

@@ -35,6 +35,10 @@ public class HsqlDb implements Dialect {
return "hsqldb";
}

public String getActiveRecordJdbcAdapter() {
return "jdbc";
}

public Class<? extends org.hibernate.dialect.Dialect> getHibernateDialectClass() {
return HSQLDialect.class;
}

+ 4
- 0
sonar-core/src/main/java/org/sonar/jpa/dialect/MsSql.java Ver fichero

@@ -36,6 +36,10 @@ public class MsSql implements Dialect {
return "sqlserver";
}

public String getActiveRecordJdbcAdapter() {
return "jdbc";
}

public Class<? extends org.hibernate.dialect.Dialect> getHibernateDialectClass() {
return MsSqlDialect.class;
}

+ 4
- 0
sonar-core/src/main/java/org/sonar/jpa/dialect/MySql.java Ver fichero

@@ -38,6 +38,10 @@ public class MySql implements Dialect {
return "mysql";
}

public String getActiveRecordJdbcAdapter() {
return "jdbc";
}

public Class<? extends org.hibernate.dialect.Dialect> getHibernateDialectClass() {
return MySqlWithDecimalDialect.class;
}

+ 4
- 0
sonar-core/src/main/java/org/sonar/jpa/dialect/Oracle.java Ver fichero

@@ -38,6 +38,10 @@ public class Oracle implements Dialect {
return "oracle";
}

public String getActiveRecordJdbcAdapter() {
return "oracle_enhanced";
}

public Class<? extends org.hibernate.dialect.Dialect> getHibernateDialectClass() {
return Oracle10gWithDecimalDialect.class;
}

+ 4
- 0
sonar-core/src/main/java/org/sonar/jpa/dialect/PostgreSql.java Ver fichero

@@ -37,6 +37,10 @@ public class PostgreSql implements Dialect {
return "postgre";
}

public String getActiveRecordJdbcAdapter() {
return "jdbc";
}

public Class<? extends org.hibernate.dialect.Dialect> getHibernateDialectClass() {
return PostgreSQLWithDecimalDialect.class;
}

+ 100
- 0
sonar-core/src/main/java/org/sonar/jpa/entity/DuplicationBlock.java Ver fichero

@@ -0,0 +1,100 @@
/*
* Sonar, open source software quality management tool.
* Copyright (C) 2008-2011 SonarSource
* mailto:contact AT sonarsource DOT com
*
* Sonar is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* Sonar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
*/
package org.sonar.jpa.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

/**
* @since 2.11
*/
@Entity
@Table(name = "duplications_index")
public class DuplicationBlock {

public static final int BLOCK_HASH_SIZE = 50;

@Id
@Column(name = "id")
@GeneratedValue
private Integer id;

@Column(name = "snapshot_id", updatable = false, nullable = false)
private Integer snapshotId;

@Column(name = "project_snapshot_id", updatable = false, nullable = false)
private Integer projectSnapshotId;

@Column(name = "hash", updatable = false, nullable = false, length = BLOCK_HASH_SIZE)
private String hash;

@Column(name = "index_in_file", updatable = false, nullable = false)
private Integer indexInFile;

@Column(name = "start_line", updatable = false, nullable = false)
private Integer startLine;

@Column(name = "end_line", updatable = false, nullable = false)
private Integer endLine;

public DuplicationBlock() {
}

public DuplicationBlock(Integer projectSnapshotId, Integer snapshotId, String hash, Integer indexInFile, Integer startLine, Integer endLine) {
this.projectSnapshotId = projectSnapshotId;
this.snapshotId = snapshotId;
this.hash = hash;
this.indexInFile = indexInFile;
this.startLine = startLine;
this.endLine = endLine;
}

public Integer getId() {
return id;
}

public Integer getSnapshotId() {
return snapshotId;
}

public Integer getProjectSnapshotId() {
return projectSnapshotId;
}

public String getHash() {
return hash;
}

public Integer getIndexInFile() {
return indexInFile;
}

public Integer getStartLine() {
return startLine;
}

public Integer getEndLine() {
return endLine;
}

}

+ 8
- 0
sonar-core/src/main/java/org/sonar/jpa/entity/ManualMeasure.java Ver fichero

@@ -19,6 +19,9 @@
*/
package org.sonar.jpa.entity;

import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

import javax.persistence.*;
import java.util.Date;

@@ -91,4 +94,9 @@ public final class ManualMeasure {
public String getUserLogin() {
return userLogin;
}

@Override
public String toString() {
return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString();
}
}

+ 6
- 0
sonar-core/src/main/java/org/sonar/jpa/entity/NotificationQueueElement.java Ver fichero

@@ -20,6 +20,8 @@
package org.sonar.jpa.entity;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.sonar.api.notifications.Notification;
import org.sonar.api.utils.SonarException;

@@ -98,4 +100,8 @@ public class NotificationQueueElement {
}
}

@Override
public String toString() {
return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString();
}
}

+ 11
- 4
sonar-core/src/main/java/org/sonar/jpa/entity/SchemaMigration.java Ver fichero

@@ -19,15 +19,17 @@
*/
package org.sonar.jpa.entity;

import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

import javax.persistence.*;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.persistence.*;

@Entity
@Table(name = SchemaMigration.TABLE_NAME, uniqueConstraints = { @UniqueConstraint(columnNames = { "version" }) })
@Table(name = SchemaMigration.TABLE_NAME, uniqueConstraints = {@UniqueConstraint(columnNames = {"version"})})
public class SchemaMigration {

public final static int VERSION_UNKNOWN = -1;
@@ -40,7 +42,7 @@ public class SchemaMigration {
- complete the Derby DDL file used for unit tests : sonar-testing-harness/src/main/resources/org/sonar/test/persistence/sonar-test.ddl

*/
public static final int LAST_VERSION = 216;
public static final int LAST_VERSION = 217;

public final static String TABLE_NAME = "schema_migrations";

@@ -102,4 +104,9 @@ public class SchemaMigration {
}
}
}

@Override
public String toString() {
return new ReflectionToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE).toString();
}
}

+ 2
- 1
sonar-core/src/main/resources/META-INF/persistence.xml Ver fichero

@@ -36,7 +36,8 @@
<class>org.sonar.api.rules.ActiveRuleParamChange</class>
<class>org.sonar.jpa.entity.Review</class>
<class>org.sonar.jpa.entity.NotificationQueueElement</class>
<class>org.sonar.jpa.entity.DuplicationBlock</class>

<properties>
<property name="hibernate.current_session_context_class" value="thread"/>
<property name="hibernate.connection.release_mode" value="after_transaction"/>

+ 8
- 0
sonar-core/src/test/java/org/sonar/core/components/CacheRuleFinderTest.java Ver fichero

@@ -24,6 +24,7 @@ import org.sonar.api.rules.Rule;
import org.sonar.api.rules.RuleFinder;
import org.sonar.jpa.test.AbstractDbUnitTestCase;

import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.Assert.assertThat;
@@ -41,6 +42,13 @@ public class CacheRuleFinderTest extends AbstractDbUnitTestCase {
assertThat(finder.findById(3), notNullValue());
}

@Test
public void shouldNotFailIfUnknownRuleId() {
setupData("shared");
RuleFinder finder = new CacheRuleFinder(getSessionFactory());
assertThat(finder.findById(33456816), nullValue());
}

@Test
public void shouldCacheFindByKey() {
setupData("shared");

+ 5
- 1
sonar-core/src/test/java/org/sonar/jpa/dialect/DialectRepositoryTest.java Ver fichero

@@ -63,8 +63,12 @@ public class DialectRepositoryTest {
}
public String getActiveRecordDialectCode() {
return null;
return "test";
}

public String getActiveRecordJdbcAdapter() {
return "jdbc";
}
}

}

+ 0
- 0
sonar-deprecated/pom.xml Ver fichero


Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio

Cargando…
Cancelar
Guardar