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 */
020package org.sonar.duplications.internal.pmd;
021
022import com.google.common.collect.Lists;
023import org.sonar.duplications.block.Block;
024import org.sonar.duplications.block.ByteArray;
025
026import java.util.Collections;
027import 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 */
034public 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}