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.duplications.internal.pmd;
021
022 import com.google.common.collect.Lists;
023 import org.sonar.duplications.block.Block;
024 import org.sonar.duplications.block.ByteArray;
025
026 import java.util.Collections;
027 import java.util.List;
028
029 /**
030 * Differences with {@link org.sonar.duplications.block.BlockChunker}:
031 * works with {@link TokensLine},
032 * sets {@link Block#getStartUnit() startUnit} and {@link Block#getEndUnit() endUnit} - indexes of first and last token for this block.
033 */
034 public class PmdBlockChunker {
035
036 private static final long PRIME_BASE = 31;
037
038 private final int blockSize;
039 private final long power;
040
041 public PmdBlockChunker(int blockSize) {
042 this.blockSize = blockSize;
043
044 long pow = 1;
045 for (int i = 0; i < blockSize - 1; i++) {
046 pow = pow * PRIME_BASE;
047 }
048 this.power = pow;
049 }
050
051 public List<Block> chunk(String resourceId, List<TokensLine> fragments) {
052 if (fragments.size() < blockSize) {
053 return Collections.emptyList();
054 }
055 TokensLine[] fragmentsArr = fragments.toArray(new TokensLine[fragments.size()]);
056 List<Block> blocks = Lists.newArrayListWithCapacity(fragmentsArr.length - blockSize + 1);
057 long hash = 0;
058 int first = 0;
059 int last = 0;
060 for (; last < blockSize - 1; last++) {
061 hash = hash * PRIME_BASE + fragmentsArr[last].getHashCode();
062 }
063 Block.Builder blockBuilder = Block.builder().setResourceId(resourceId);
064 for (; last < fragmentsArr.length; last++, first++) {
065 TokensLine firstFragment = fragmentsArr[first];
066 TokensLine lastFragment = fragmentsArr[last];
067 // add last statement to hash
068 hash = hash * PRIME_BASE + lastFragment.getHashCode();
069 // create block
070 Block block = blockBuilder
071 .setBlockHash(new ByteArray(hash))
072 .setIndexInFile(first)
073 .setLines(firstFragment.getStartLine(), lastFragment.getEndLine())
074 .setUnit(firstFragment.getStartUnit(), lastFragment.getEndUnit())
075 .build();
076 blocks.add(block);
077 // remove first statement from hash
078 hash -= power * firstFragment.getHashCode();
079 }
080 return blocks;
081 }
082
083 }