You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SonarEngine.java 7.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /*
  2. * Sonar, open source software quality management tool.
  3. * Copyright (C) 2008-2011 SonarSource
  4. * mailto:contact AT sonarsource DOT com
  5. *
  6. * Sonar is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * Sonar is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public
  17. * License along with Sonar; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
  19. */
  20. package org.sonar.plugins.cpd;
  21. import java.io.FileInputStream;
  22. import java.io.FileNotFoundException;
  23. import java.io.InputStreamReader;
  24. import java.io.Reader;
  25. import java.util.Collection;
  26. import java.util.List;
  27. import java.util.concurrent.*;
  28. import org.apache.commons.io.IOUtils;
  29. import org.apache.commons.lang.StringUtils;
  30. import org.sonar.api.CoreProperties;
  31. import org.sonar.api.batch.SensorContext;
  32. import org.sonar.api.database.DatabaseSession;
  33. import org.sonar.api.database.model.ResourceModel;
  34. import org.sonar.api.resources.*;
  35. import org.sonar.api.utils.Logs;
  36. import org.sonar.api.utils.SonarException;
  37. import org.sonar.batch.index.ResourcePersister;
  38. import org.sonar.duplications.block.Block;
  39. import org.sonar.duplications.block.BlockChunker;
  40. import org.sonar.duplications.detector.original.OriginalCloneDetectionAlgorithm;
  41. import org.sonar.duplications.index.CloneGroup;
  42. import org.sonar.duplications.index.CloneIndex;
  43. import org.sonar.duplications.index.ClonePart;
  44. import org.sonar.duplications.java.JavaStatementBuilder;
  45. import org.sonar.duplications.java.JavaTokenProducer;
  46. import org.sonar.duplications.statement.Statement;
  47. import org.sonar.duplications.statement.StatementChunker;
  48. import org.sonar.duplications.token.TokenChunker;
  49. import org.sonar.plugins.cpd.index.DbDuplicationsIndex;
  50. import org.sonar.plugins.cpd.index.SonarDuplicationsIndex;
  51. public class SonarEngine extends CpdEngine {
  52. private static final int BLOCK_SIZE = 10;
  53. /**
  54. * Limit of time to analyse one file (in seconds).
  55. */
  56. private static final int TIMEOUT = 5 * 60;
  57. private final ResourcePersister resourcePersister;
  58. private final DatabaseSession dbSession;
  59. /**
  60. * For dry run, where is no access to database.
  61. */
  62. public SonarEngine() {
  63. this(null, null);
  64. }
  65. public SonarEngine(ResourcePersister resourcePersister, DatabaseSession dbSession) {
  66. this.resourcePersister = resourcePersister;
  67. this.dbSession = dbSession;
  68. }
  69. @Override
  70. public boolean isLanguageSupported(Language language) {
  71. return Java.INSTANCE.equals(language);
  72. }
  73. /**
  74. * @return true, if was enabled by user and database is available
  75. */
  76. private boolean isCrossProject(Project project) {
  77. return project.getConfiguration().getBoolean(CoreProperties.CPD_CROSS_RPOJECT, CoreProperties.CPD_CROSS_RPOJECT_DEFAULT_VALUE)
  78. && resourcePersister != null && dbSession != null
  79. && StringUtils.isBlank(project.getConfiguration().getString(CoreProperties.PROJECT_BRANCH_PROPERTY));
  80. }
  81. private static String getFullKey(Project project, Resource resource) {
  82. return new StringBuilder(ResourceModel.KEY_SIZE)
  83. .append(project.getKey())
  84. .append(':')
  85. .append(resource.getKey())
  86. .toString();
  87. }
  88. @Override
  89. public void analyse(Project project, SensorContext context) {
  90. List<InputFile> inputFiles = project.getFileSystem().mainFiles(project.getLanguageKey());
  91. if (inputFiles.isEmpty()) {
  92. return;
  93. }
  94. // Create index
  95. final SonarDuplicationsIndex index;
  96. if (isCrossProject(project)) {
  97. Logs.INFO.info("Cross-project analysis enabled");
  98. index = new SonarDuplicationsIndex(new DbDuplicationsIndex(dbSession, resourcePersister, project));
  99. } else {
  100. Logs.INFO.info("Cross-project analysis disabled");
  101. index = new SonarDuplicationsIndex();
  102. }
  103. TokenChunker tokenChunker = JavaTokenProducer.build();
  104. StatementChunker statementChunker = JavaStatementBuilder.build();
  105. BlockChunker blockChunker = new BlockChunker(BLOCK_SIZE);
  106. for (InputFile inputFile : inputFiles) {
  107. Resource resource = getResource(inputFile);
  108. String resourceKey = getFullKey(project, resource);
  109. List<Statement> statements;
  110. Reader reader = null;
  111. try {
  112. reader = new InputStreamReader(new FileInputStream(inputFile.getFile()), project.getFileSystem().getSourceCharset());
  113. statements = statementChunker.chunk(tokenChunker.chunk(reader));
  114. } catch (FileNotFoundException e) {
  115. throw new SonarException(e);
  116. } finally {
  117. IOUtils.closeQuietly(reader);
  118. }
  119. List<Block> blocks = blockChunker.chunk(resourceKey, statements);
  120. index.insert(resource, blocks);
  121. }
  122. // Detect
  123. ExecutorService executorService = Executors.newSingleThreadExecutor();
  124. try {
  125. for (InputFile inputFile : inputFiles) {
  126. Logs.INFO.debug("Detection of duplications for {}", inputFile.getFile());
  127. Resource resource = getResource(inputFile);
  128. String resourceKey = getFullKey(project, resource);
  129. Collection<Block> fileBlocks = index.getByResource(resource, resourceKey);
  130. List<CloneGroup> clones;
  131. try {
  132. clones = executorService.submit(new Task(index, fileBlocks)).get(TIMEOUT, TimeUnit.SECONDS);
  133. } catch (TimeoutException e) {
  134. clones = null;
  135. Logs.INFO.warn("Timeout during detection of duplications for " + inputFile.getFile(), e);
  136. } catch (InterruptedException e) {
  137. throw new SonarException(e);
  138. } catch (ExecutionException e) {
  139. throw new SonarException(e);
  140. }
  141. if (clones != null && !clones.isEmpty()) {
  142. // Save
  143. DuplicationsData data = new DuplicationsData(resource, context);
  144. for (CloneGroup clone : clones) {
  145. poplulateData(data, clone);
  146. }
  147. data.save();
  148. }
  149. }
  150. } finally {
  151. executorService.shutdown();
  152. }
  153. }
  154. private static class Task implements Callable<List<CloneGroup>> {
  155. private final CloneIndex index;
  156. private final Collection<Block> fileBlocks;
  157. public Task(CloneIndex index, Collection<Block> fileBlocks) {
  158. this.index = index;
  159. this.fileBlocks = fileBlocks;
  160. }
  161. public List<CloneGroup> call() {
  162. return OriginalCloneDetectionAlgorithm.detect(index, fileBlocks);
  163. }
  164. }
  165. private Resource getResource(InputFile inputFile) {
  166. return JavaFile.fromRelativePath(inputFile.getRelativePath(), false);
  167. }
  168. private void poplulateData(DuplicationsData data, CloneGroup clone) {
  169. ClonePart origin = clone.getOriginPart();
  170. int originLines = origin.getLineEnd() - origin.getLineStart() + 1;
  171. data.incrementDuplicatedBlock();
  172. for (ClonePart part : clone.getCloneParts()) {
  173. if (part.equals(origin)) {
  174. continue;
  175. }
  176. data.cumulate(part.getResourceId(), part.getLineStart(), origin.getLineStart(), originLines);
  177. if (part.getResourceId().equals(origin.getResourceId())) {
  178. data.incrementDuplicatedBlock();
  179. data.cumulate(origin.getResourceId(), origin.getLineStart(), part.getLineStart(), originLines);
  180. }
  181. }
  182. }
  183. }