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    }