aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-db/src/main/java/org/sonar/db/component/ResourceKeyUpdaterDao.java
blob: 4c0fb971c341b823699db3846b6be287540bfcc6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/*
 * SonarQube
 * Copyright (C) 2009-2016 SonarSource SA
 * mailto:contact AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.db.component;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.apache.ibatis.session.SqlSession;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;
import org.sonar.db.MyBatis;

/**
 * Class used to rename the key of a project and its resources.
 *
 * @since 3.2
 */
public class ResourceKeyUpdaterDao implements Dao {
  private MyBatis mybatis;

  public ResourceKeyUpdaterDao(MyBatis mybatis) {
    this.mybatis = mybatis;
  }

  public void updateKey(String projectUuid, String newKey) {
    DbSession session = mybatis.openSession(true);
    ResourceKeyUpdaterMapper mapper = session.getMapper(ResourceKeyUpdaterMapper.class);
    try {
      if (mapper.countResourceByKey(newKey) > 0) {
        throw new IllegalArgumentException("Impossible to update key: a component with key \"" + newKey + "\" already exists.");
      }

      // must SELECT first everything
      ResourceDto project = mapper.selectProject(projectUuid);
      String projectOldKey = project.getKey();
      List<ResourceDto> resources = mapper.selectProjectResources(projectUuid);
      resources.add(project);

      // and then proceed with the batch UPDATE at once
      runBatchUpdateForAllResources(resources, projectOldKey, newKey, mapper);

      session.commit();
    } finally {
      MyBatis.closeQuietly(session);
    }
  }

  public Map<String, String> checkModuleKeysBeforeRenaming(String projectUuid, String stringToReplace, String replacementString) {
    SqlSession session = mybatis.openSession(false);
    ResourceKeyUpdaterMapper mapper = session.getMapper(ResourceKeyUpdaterMapper.class);
    Map<String, String> result = Maps.newHashMap();
    try {
      Set<ResourceDto> modules = collectAllModules(projectUuid, stringToReplace, mapper);
      for (ResourceDto module : modules) {
        String newKey = computeNewKey(module, stringToReplace, replacementString);
        if (mapper.countResourceByKey(newKey) > 0) {
          result.put(module.getKey(), "#duplicate_key#");
        } else {
          result.put(module.getKey(), newKey);
        }
      }
    } finally {
      MyBatis.closeQuietly(session);
    }
    return result;
  }

  public void bulkUpdateKey(DbSession session, String projectUuid, String stringToReplace, String replacementString) {
    ResourceKeyUpdaterMapper mapper = session.getMapper(ResourceKeyUpdaterMapper.class);
    // must SELECT first everything
    Set<ResourceDto> modules = collectAllModules(projectUuid, stringToReplace, mapper);
    checkNewNameOfAllModules(modules, stringToReplace, replacementString, mapper);
    Map<ResourceDto, List<ResourceDto>> allResourcesByModuleMap = Maps.newHashMap();
    for (ResourceDto module : modules) {
      allResourcesByModuleMap.put(module, mapper.selectProjectResources(module.getUuid()));
    }

    // and then proceed with the batch UPDATE at once
    for (ResourceDto module : modules) {
      String oldModuleKey = module.getKey();
      String newModuleKey = computeNewKey(module, stringToReplace, replacementString);
      Collection<ResourceDto> resources = Lists.newArrayList(module);
      resources.addAll(allResourcesByModuleMap.get(module));
      runBatchUpdateForAllResources(resources, oldModuleKey, newModuleKey, mapper);
    }
  }

  private static String computeNewKey(ResourceDto resource, String stringToReplace, String replacementString) {
    return resource.getKey().replaceAll(stringToReplace, replacementString);
  }

  private static void runBatchUpdateForAllResources(Collection<ResourceDto> resources, String oldKey, String newKey, ResourceKeyUpdaterMapper mapper) {
    for (ResourceDto resource : resources) {
      String oldResourceKey = resource.getKey();
      String newResourceKey = newKey + oldResourceKey.substring(oldKey.length(), oldResourceKey.length());
      resource.setKey(newResourceKey);
      String oldResourceDeprecatedKey = resource.getDeprecatedKey();
      if (StringUtils.isNotBlank(oldResourceDeprecatedKey)) {
        String newResourceDeprecatedKey = newKey + oldResourceDeprecatedKey.substring(oldKey.length(), oldResourceDeprecatedKey.length());
        resource.setDeprecatedKey(newResourceDeprecatedKey);
      }
      mapper.update(resource);
    }
  }

  private static Set<ResourceDto> collectAllModules(String projectUuid, String stringToReplace, ResourceKeyUpdaterMapper mapper) {
    ResourceDto project = mapper.selectProject(projectUuid);
    Set<ResourceDto> modules = Sets.newHashSet();
    if (project.getKey().contains(stringToReplace)) {
      modules.add(project);
    }
    for (ResourceDto submodule : mapper.selectDescendantProjects(projectUuid)) {
      modules.addAll(collectAllModules(submodule.getUuid(), stringToReplace, mapper));
    }
    return modules;
  }

  private static void checkNewNameOfAllModules(Set<ResourceDto> modules, String stringToReplace, String replacementString, ResourceKeyUpdaterMapper mapper) {
    for (ResourceDto module : modules) {
      String newName = computeNewKey(module, stringToReplace, replacementString);
      if (mapper.countResourceByKey(newName) > 0) {
        throw new IllegalStateException("Impossible to update key: a resource with \"" + newName + "\" key already exists.");
      }
    }
  }

}