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.plugins.cpd.index;
021
022import com.google.common.collect.Lists;
023import com.google.common.collect.Maps;
024import org.sonar.api.database.model.Snapshot;
025import org.sonar.api.resources.Project;
026import org.sonar.api.resources.Resource;
027import org.sonar.batch.index.ResourcePersister;
028import org.sonar.core.duplication.DuplicationDao;
029import org.sonar.core.duplication.DuplicationUnitDto;
030import org.sonar.duplications.block.Block;
031import org.sonar.duplications.block.ByteArray;
032
033import java.util.Collection;
034import java.util.Collections;
035import java.util.List;
036import java.util.Map;
037
038public class DbDuplicationsIndex {
039
040  private final Map<ByteArray, Collection<Block>> cache = Maps.newHashMap();
041
042  private final ResourcePersister resourcePersister;
043  private final int currentProjectSnapshotId;
044  private final Integer lastSnapshotId;
045  private final String languageKey;
046
047  private DuplicationDao dao;
048
049  public DbDuplicationsIndex(ResourcePersister resourcePersister, Project currentProject, DuplicationDao dao) {
050    this.dao = dao;
051    this.resourcePersister = resourcePersister;
052    Snapshot currentSnapshot = resourcePersister.getSnapshotOrFail(currentProject);
053    Snapshot lastSnapshot = resourcePersister.getLastSnapshot(currentSnapshot, false);
054    this.currentProjectSnapshotId = currentSnapshot.getId();
055    this.lastSnapshotId = lastSnapshot == null ? null : lastSnapshot.getId();
056    this.languageKey = currentProject.getLanguageKey();
057  }
058
059  int getSnapshotIdFor(Resource resource) {
060    return resourcePersister.getSnapshotOrFail(resource).getId();
061  }
062
063  public void prepareCache(Resource resource) {
064    int resourceSnapshotId = getSnapshotIdFor(resource);
065    List<DuplicationUnitDto> units = dao.selectCandidates(resourceSnapshotId, lastSnapshotId, languageKey);
066    cache.clear();
067    // TODO Godin: maybe remove conversion of units to blocks?
068    for (DuplicationUnitDto unit : units) {
069      String hash = unit.getHash();
070      String resourceKey = unit.getResourceKey();
071      int indexInFile = unit.getIndexInFile();
072      int startLine = unit.getStartLine();
073      int endLine = unit.getEndLine();
074
075      // TODO Godin: in fact we could work directly with id instead of key - this will allow to decrease memory consumption
076      Block block = Block.builder()
077          .setResourceId(resourceKey)
078          .setBlockHash(new ByteArray(hash))
079          .setIndexInFile(indexInFile)
080          .setLines(startLine, endLine)
081          .build();
082
083      // Group blocks by hash
084      Collection<Block> sameHash = cache.get(block.getBlockHash());
085      if (sameHash == null) {
086        sameHash = Lists.newArrayList();
087        cache.put(block.getBlockHash(), sameHash);
088      }
089      sameHash.add(block);
090    }
091  }
092
093  public Collection<Block> getByHash(ByteArray hash) {
094    Collection<Block> result = cache.get(hash);
095    if (result != null) {
096      return result;
097    } else {
098      return Collections.emptyList();
099    }
100  }
101
102  public void insert(Resource resource, Collection<Block> blocks) {
103    int resourceSnapshotId = getSnapshotIdFor(resource);
104
105    // TODO Godin: maybe remove conversion of blocks to units?
106    List<DuplicationUnitDto> units = Lists.newArrayList();
107    for (Block block : blocks) {
108      DuplicationUnitDto unit = new DuplicationUnitDto(
109          currentProjectSnapshotId,
110          resourceSnapshotId,
111          block.getBlockHash().toString(),
112          block.getIndexInFile(),
113          block.getStartLine(),
114          block.getEndLine());
115      units.add(unit);
116    }
117
118    dao.insert(units);
119  }
120
121}