001 /*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02
019 */
020 package org.sonar.plugins.cpd;
021
022 import com.google.common.annotations.VisibleForTesting;
023 import com.google.common.base.Predicate;
024 import com.google.common.collect.Iterables;
025 import org.sonar.api.batch.CpdMapping;
026 import org.sonar.api.batch.SensorContext;
027 import org.sonar.api.resources.*;
028 import org.sonar.duplications.DuplicationPredicates;
029 import org.sonar.duplications.block.Block;
030 import org.sonar.duplications.detector.suffixtree.SuffixTreeCloneDetectionAlgorithm;
031 import org.sonar.duplications.index.CloneGroup;
032 import org.sonar.duplications.internal.pmd.TokenizerBridge;
033 import org.sonar.plugins.cpd.index.IndexFactory;
034 import org.sonar.plugins.cpd.index.SonarDuplicationsIndex;
035
036 import java.util.Collection;
037 import java.util.List;
038
039 public class SonarBridgeEngine extends CpdEngine {
040
041 private final IndexFactory indexFactory;
042 private final CpdMapping[] mappings;
043
044 public SonarBridgeEngine(IndexFactory indexFactory) {
045 this.indexFactory = indexFactory;
046 this.mappings = null;
047 }
048
049 public SonarBridgeEngine(IndexFactory indexFactory, CpdMapping[] mappings) {
050 this.indexFactory = indexFactory;
051 this.mappings = mappings;
052 }
053
054 @Override
055 public boolean isLanguageSupported(Language language) {
056 return getMapping(language) != null;
057 }
058
059 @Override
060 public void analyse(Project project, SensorContext context) {
061 ProjectFileSystem fileSystem = project.getFileSystem();
062 List<InputFile> inputFiles = fileSystem.mainFiles(project.getLanguageKey());
063 if (inputFiles.isEmpty()) {
064 return;
065 }
066
067 CpdMapping mapping = getMapping(project.getLanguage());
068
069 // Create index
070 SonarDuplicationsIndex index = indexFactory.create(project);
071
072 TokenizerBridge bridge = new TokenizerBridge(mapping.getTokenizer(), fileSystem.getSourceCharset().name(), getBlockSize(project));
073 for (InputFile inputFile : inputFiles) {
074 Resource resource = mapping.createResource(inputFile.getFile(), fileSystem.getSourceDirs());
075 String resourceId = SonarEngine.getFullKey(project, resource);
076 List<Block> blocks = bridge.chunk(resourceId, inputFile.getFile());
077 index.insert(resource, blocks);
078 }
079
080 // Detect
081 Predicate<CloneGroup> minimumTokensPredicate = DuplicationPredicates.numberOfUnitsNotLessThan(PmdEngine.getMinimumTokens(project));
082
083 for (InputFile inputFile : inputFiles) {
084 Resource resource = mapping.createResource(inputFile.getFile(), fileSystem.getSourceDirs());
085 String resourceKey = SonarEngine.getFullKey(project, resource);
086
087 Collection<Block> fileBlocks = index.getByResource(resource, resourceKey);
088 List<CloneGroup> duplications = SuffixTreeCloneDetectionAlgorithm.detect(index, fileBlocks);
089
090 Iterable<CloneGroup> filtered = Iterables.filter(duplications, minimumTokensPredicate);
091
092 SonarEngine.save(context, resource, filtered);
093 }
094 }
095
096 private static int getBlockSize(Project project) {
097 String languageKey = project.getLanguageKey();
098 return project.getConfiguration()
099 .getInt("sonar.cpd." + languageKey + ".minimumLines", getDefaultBlockSize(languageKey));
100 }
101
102 @VisibleForTesting
103 static int getDefaultBlockSize(String languageKey) {
104 if ("cobol".equals(languageKey)) {
105 return 30;
106 } else if ("abap".equals(languageKey) || "natur".equals(languageKey)) {
107 return 20;
108 } else {
109 return 10;
110 }
111 }
112
113 private CpdMapping getMapping(Language language) {
114 if (mappings != null) {
115 for (CpdMapping cpdMapping : mappings) {
116 if (cpdMapping.getLanguage().equals(language)) {
117 return cpdMapping;
118 }
119 }
120 }
121 return null;
122 }
123
124 }