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 }