+ allow to replace any string of the key, not only the beginning.tags/3.2
@@ -969,15 +969,16 @@ update_key.cant_update_x_because_resource_already_exist_with_key_x="{0}" can not | |||
update_key.error_occured_while_renaming_key_of_x=An error occurred while renaming the key "{0}": {1} | |||
update_key.key_updated=The key has successfully been updated for all required resources. | |||
update_key.fieds_cant_be_blank_for_bulk_update=The two fields can not be blank for the bulk update. | |||
update_key.key_does_not_start_with_x=The current key ("{0}") does not start with "{1}". The update is impossible. | |||
update_key.bulk_change_description=The bulk update allows to replace the beginning of the current key by another string on the current project and all its submodules - if applicable. | |||
update_key.current_key_for_project_x_is_x=The key of the "{0}" project is currently "<b>{1}</b>". | |||
update_key.are_you_sure_to_rename_x=Are you sure you want to rename "{0}", as well as all its modules and resources ? | |||
update_key.are_you_sure_to_bulk_rename_x_into_x=Are you sure you want to rename "<b>{0}</b>{1}" into "<b>{2}</b>{1}", as well as all its modules and resources ? | |||
update_key.replace=Replace | |||
update_key.by=By | |||
update_key.replace_example=Ex.: "org.myCompany" | |||
update_key.by_example=Ex.: "com.myNewCompany" | |||
update_key.cant_update_because_duplicate_keys=The replacement of "{0}" by "{1}" is impossible as it would result in duplicate keys (in red below): | |||
update_key.keys_will_be_updated_as_follows=The resources will be updated as follows (updated keys in bold): | |||
update_key.duplicate_key=Duplicate key | |||
#------------------------------------------------------------------------------ |
@@ -19,12 +19,15 @@ | |||
*/ | |||
package org.sonar.core.resource; | |||
import com.google.common.collect.Lists; | |||
import com.google.common.collect.Maps; | |||
import com.google.common.collect.Sets; | |||
import org.apache.ibatis.session.SqlSession; | |||
import org.sonar.core.persistence.MyBatis; | |||
import java.util.Collection; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
/** | |||
@@ -62,20 +65,46 @@ public class ResourceKeyUpdaterDao { | |||
} | |||
} | |||
public void bulkUpdateKey(long projectId, String oldPrefix, String newPrefix) { | |||
public Map<String, String> checkModuleKeysBeforeRenaming(long projectId, String stringToReplace, String replacementString) { | |||
SqlSession session = mybatis.openSession(); | |||
ResourceKeyUpdaterMapper mapper = session.getMapper(ResourceKeyUpdaterMapper.class); | |||
Map<String, String> result = Maps.newHashMap(); | |||
try { | |||
Set<ResourceDto> modules = collectAllModules(projectId, 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(long projectId, String stringToReplace, String replacementString) { | |||
SqlSession session = mybatis.openBatchSession(); | |||
ResourceKeyUpdaterMapper mapper = session.getMapper(ResourceKeyUpdaterMapper.class); | |||
try { | |||
// must SELECT first everything | |||
Set<ResourceDto> modules = collectAllModules(projectId, oldPrefix, mapper); | |||
checkNewNameOfAllModules(modules, oldPrefix, newPrefix, mapper); | |||
Set<ResourceDto> allResources = Sets.newHashSet(modules); | |||
Set<ResourceDto> modules = collectAllModules(projectId, stringToReplace, mapper); | |||
checkNewNameOfAllModules(modules, stringToReplace, replacementString, mapper); | |||
Map<ResourceDto, List<ResourceDto>> allResourcesByModuleMap = Maps.newHashMap(); | |||
for (ResourceDto module : modules) { | |||
allResources.addAll(mapper.selectProjectResources(module.getId())); | |||
allResourcesByModuleMap.put(module, mapper.selectProjectResources(module.getId())); | |||
} | |||
// and then proceed with the batch UPDATE at once | |||
runBatchUpdateForAllResources(allResources, oldPrefix, newPrefix, mapper); | |||
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); | |||
} | |||
session.commit(); | |||
} finally { | |||
@@ -83,33 +112,33 @@ public class ResourceKeyUpdaterDao { | |||
} | |||
} | |||
private String computeNewKey(ResourceDto resource, String oldPrefix, String newPrefix) { | |||
String resourceKey = resource.getKey(); | |||
return newPrefix + resourceKey.substring(oldPrefix.length(), resourceKey.length()); | |||
private String computeNewKey(ResourceDto resource, String stringToReplace, String replacementString) { | |||
return resource.getKey().replaceAll(stringToReplace, replacementString); | |||
} | |||
private void runBatchUpdateForAllResources(Collection<ResourceDto> resources, String oldPrefix, String newPrefix, ResourceKeyUpdaterMapper mapper) { | |||
private void runBatchUpdateForAllResources(Collection<ResourceDto> resources, String oldKey, String newKey, ResourceKeyUpdaterMapper mapper) { | |||
for (ResourceDto resource : resources) { | |||
resource.setKey(computeNewKey(resource, oldPrefix, newPrefix)); | |||
String resourceKey = resource.getKey(); | |||
resource.setKey(newKey + resourceKey.substring(oldKey.length(), resourceKey.length())); | |||
mapper.update(resource); | |||
} | |||
} | |||
private Set<ResourceDto> collectAllModules(long projectId, String oldPrefix, ResourceKeyUpdaterMapper mapper) { | |||
private Set<ResourceDto> collectAllModules(long projectId, String stringToReplace, ResourceKeyUpdaterMapper mapper) { | |||
ResourceDto project = mapper.selectProject(projectId); | |||
Set<ResourceDto> modules = Sets.newHashSet(); | |||
if (project.getKey().startsWith(oldPrefix)) { | |||
if (project.getKey().contains(stringToReplace)) { | |||
modules.add(project); | |||
} | |||
for (ResourceDto submodule : mapper.selectDescendantProjects(projectId)) { | |||
modules.addAll(collectAllModules(submodule.getId(), oldPrefix, mapper)); | |||
modules.addAll(collectAllModules(submodule.getId(), stringToReplace, mapper)); | |||
} | |||
return modules; | |||
} | |||
private void checkNewNameOfAllModules(Set<ResourceDto> modules, String oldPrefix, String newPrefix, ResourceKeyUpdaterMapper mapper) { | |||
private void checkNewNameOfAllModules(Set<ResourceDto> modules, String stringToReplace, String replacementString, ResourceKeyUpdaterMapper mapper) { | |||
for (ResourceDto module : modules) { | |||
String newName = computeNewKey(module, oldPrefix, newPrefix); | |||
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."); | |||
} |
@@ -25,6 +25,10 @@ import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.core.persistence.DaoTestCase; | |||
import java.util.Map; | |||
import static org.fest.assertions.Assertions.assertThat; | |||
public class ResourceKeyUpdaterDaoTest extends DaoTestCase { | |||
@Rule | |||
@@ -65,6 +69,15 @@ public class ResourceKeyUpdaterDaoTest extends DaoTestCase { | |||
checkTables("shouldBulkUpdateKey", "projects"); | |||
} | |||
@Test | |||
public void shouldBulkUpdateKeyOnOnlyOneSubmodule() { | |||
setupData("shared"); | |||
dao.bulkUpdateKey(1, "struts-ui", "struts-web"); | |||
checkTables("shouldBulkUpdateKeyOnOnlyOneSubmodule", "projects"); | |||
} | |||
@Test | |||
public void shouldFailBulkUpdateKeyIfKeyAlreadyExist() { | |||
setupData("shared"); | |||
@@ -84,4 +97,15 @@ public class ResourceKeyUpdaterDaoTest extends DaoTestCase { | |||
checkTables("shouldNotUpdateAllSubmodules", "projects"); | |||
} | |||
@Test | |||
public void shouldCheckModuleKeysBeforeRenaming() { | |||
setupData("shared"); | |||
Map<String, String> checkResults = dao.checkModuleKeysBeforeRenaming(1, "org.struts", "foo"); | |||
assertThat(checkResults.size()).isEqualTo(3); | |||
assertThat(checkResults.get("org.struts:struts")).isEqualTo("foo:struts"); | |||
assertThat(checkResults.get("org.struts:struts-core")).isEqualTo("update_impossible"); | |||
assertThat(checkResults.get("org.struts:struts-ui")).isEqualTo("foo:struts-ui"); | |||
} | |||
} |
@@ -0,0 +1,50 @@ | |||
<dataset> | |||
<!-- root project --> | |||
<projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts" | |||
description="[null]" long_name="Apache Struts" profile_id="1" | |||
enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/> | |||
<!-- **************** First sub project **************** --> | |||
<projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core" | |||
scope="PRJ" qualifier="BRC" long_name="Struts Core" profile_id="1" | |||
description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/> | |||
<!-- directory --> | |||
<projects long_name="org.struts" id="3" scope="DIR" qualifier="PAC" kee="org.struts:struts-core:org.struts" | |||
name="org.struts" root_id="2" | |||
description="[null]" profile_id="1" | |||
enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/> | |||
<!-- file --> | |||
<projects long_name="org.struts.RequestContext" id="4" scope="FIL" qualifier="CLA" kee="org.struts:struts-core:org.struts.RequestContext" | |||
name="RequestContext" root_id="2" | |||
description="[null]" profile_id="1" | |||
enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/> | |||
<!-- **************** Second sub project **************** --> | |||
<projects id="5" root_id="1" kee="org.struts:struts-web" name="Struts UI" | |||
scope="PRJ" qualifier="BRC" long_name="Struts UI" profile_id="1" | |||
description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/> | |||
<!-- directory --> | |||
<projects long_name="org.struts" id="6" scope="DIR" qualifier="PAC" kee="org.struts:struts-web:org.struts" | |||
name="org.struts" root_id="5" | |||
description="[null]" profile_id="1" | |||
enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/> | |||
<!-- file --> | |||
<projects long_name="org.struts.RequestContext" id="7" scope="FIL" qualifier="CLA" kee="org.struts:struts-web:org.struts.RequestContext" | |||
name="RequestContext" root_id="5" | |||
description="[null]" profile_id="1" | |||
enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/> | |||
<!-- **************** Another independent project **************** --> | |||
<projects id="8" root_id="[null]" kee="foo:struts-core" name="Foo Struts Core" | |||
scope="PRJ" qualifier="BRC" long_name="Foo Struts Core" profile_id="1" | |||
description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/> | |||
</dataset> |
@@ -516,8 +516,12 @@ public final class JRubyFacade { | |||
getContainer().getComponentByType(ResourceKeyUpdaterDao.class).updateKey(projectId, newKey); | |||
} | |||
public void bulkUpdateKey(long projectId, String oldPrefix, String newPrefix) { | |||
getContainer().getComponentByType(ResourceKeyUpdaterDao.class).bulkUpdateKey(projectId, oldPrefix, newPrefix); | |||
public Map<String, String> checkModuleKeysBeforeRenaming(long projectId, String stringToReplace, String replacementString) { | |||
return getContainer().getComponentByType(ResourceKeyUpdaterDao.class).checkModuleKeysBeforeRenaming(projectId, stringToReplace, replacementString); | |||
} | |||
public void bulkUpdateKey(long projectId, String stringToReplace, String replacementString) { | |||
getContainer().getComponentByType(ResourceKeyUpdaterDao.class).bulkUpdateKey(projectId, stringToReplace, replacementString); | |||
} | |||
} |
@@ -71,11 +71,6 @@ class ProjectController < ApplicationController | |||
def key | |||
@project = get_current_project(params[:id]) | |||
if params[:old_prefix] && params[:new_prefix] | |||
@old_prefix = params[:old_prefix] | |||
@new_prefix = params[:new_prefix] | |||
end | |||
end | |||
def update_key | |||
@@ -102,27 +97,20 @@ class ProjectController < ApplicationController | |||
end | |||
def prepare_key_bulk_update | |||
project = get_current_project(params[:id]) | |||
@project = get_current_project(params[:id]) | |||
old_prefix = params[:old_prefix].strip | |||
new_prefix = params[:new_prefix].strip | |||
if old_prefix.blank? || new_prefix.blank? | |||
error_message = message('update_key.fieds_cant_be_blank_for_bulk_update') | |||
elsif !project.key.start_with?(old_prefix) | |||
error_message = message('update_key.key_does_not_start_with_x', :params => [project.key, old_prefix]) | |||
@old_prefix = params[:old_prefix].strip | |||
@new_prefix = params[:new_prefix].strip | |||
if @old_prefix.blank? || @new_prefix.blank? | |||
flash[:error] = message('update_key.fieds_cant_be_blank_for_bulk_update') | |||
redirect_to :action => 'key', :id => @project.id | |||
else | |||
new_key = new_prefix + project.key[old_prefix.size..project.key.size] | |||
if Project.by_key(new_key) | |||
error_message = message('update_key.cant_update_x_because_resource_already_exist_with_key_x', :params => [project.key, new_key]) | |||
@key_check_results = java_facade.checkModuleKeysBeforeRenaming(@project.id, @old_prefix, @new_prefix) | |||
@can_update = true | |||
@key_check_results.each do |key, value| | |||
@can_update = false if value=="#duplicate_key#" | |||
end | |||
end | |||
if error_message | |||
flash[:error] = error_message | |||
redirect_to :action => 'key', :id => project.id | |||
else | |||
redirect_to :action => 'key', :id => project.id, :old_prefix => old_prefix, :new_prefix => new_prefix | |||
end | |||
end | |||
def perform_key_bulk_update | |||
@@ -131,7 +119,7 @@ class ProjectController < ApplicationController | |||
old_prefix = params[:old_prefix].strip | |||
new_prefix = params[:new_prefix].strip | |||
unless old_prefix.blank? || new_prefix.blank? || !project.key.start_with?(old_prefix) | |||
unless old_prefix.blank? || new_prefix.blank? | |||
begin | |||
java_facade.bulkUpdateKey(project.id, old_prefix, new_prefix) | |||
flash[:notice] = message('update_key.key_updated') |
@@ -0,0 +1,19 @@ | |||
<% | |||
future_key = key_check_results[current_module.key] | |||
if future_key=="#duplicate_key#" | |||
duplicate_key = true | |||
future_key = message('update_key.duplicate_key') | |||
end | |||
%> | |||
<tr class="<%= cycle 'even', 'odd', :name => 'modules_tree' -%>"> | |||
<td class="thin nowrap" style="padding-left: <%= 3+ module_depth*15 -%>px;"> | |||
<%= h(current_module.key) -%> | |||
</td> | |||
<td class="thin nowrap <%= 'error' if duplicate_key -%>" style="<%= 'font-weight: bold;' if future_key -%>"> | |||
<%= future_key ? future_key : current_module.key -%> | |||
</td> | |||
</tr> | |||
<% current_module.modules.each_with_index do |sub_module, index| %> | |||
<%= render :partial => 'prepare_keys', :locals => {:current_module => sub_module, :module_depth => module_depth+1, | |||
:key_check_results => key_check_results} -%> | |||
<% end %> |
@@ -1,6 +1,7 @@ | |||
<% | |||
if controller.java_facade.getResourceTypeBooleanProperty(@project.qualifier, 'updatable_key') | |||
has_modules = !@project.modules.empty? | |||
reset_cycle 'modules_tree' | |||
%> | |||
<script type="text/javascript"> | |||
@@ -11,34 +12,6 @@ | |||
} | |||
</script> | |||
<% | |||
if @old_prefix && @new_prefix | |||
# validation screen for bulk update | |||
key_suffix = @project.key[@old_prefix.size..@project.key.size] | |||
%> | |||
<h1><%= message('update_key.bulk_update') -%></h1> | |||
<br/> | |||
<p> | |||
<%= message('update_key.are_you_sure_to_bulk_rename_x_into_x', :params => [@old_prefix, key_suffix, @new_prefix]) -%> | |||
</p> | |||
<% form_tag( {:action => 'perform_key_bulk_update', :id => @project.id }) do -%> | |||
<input type="hidden" value="<%= @project.id -%>" name="id" id="project_id"> | |||
<input type="hidden" value="<%= @old_prefix -%>" name="old_prefix" id="old_prefix"> | |||
<input type="hidden" value="<%= @new_prefix -%>" name="new_prefix" id="new_prefix"> | |||
<br/> | |||
<%= submit_tag message('update_key.rename'), :id => 'bulk_update_button', :class => 'action', | |||
:onclick => "update_launched();$('loading_bulk_update').show();" %> | |||
<a href="<%= url_for :action => 'key', :id => @project.key -%>"><%= message('cancel') -%></a> | |||
<span class="loading" id="loading_bulk_update" style="display: none; padding: 3px 10px;"></span> | |||
<% end %> | |||
<% | |||
else | |||
# main screen | |||
reset_cycle 'modules_tree' | |||
%> | |||
<% if has_modules %> | |||
<h1><%= message('update_key.bulk_update') -%></h1> | |||
<br/> | |||
@@ -77,7 +50,7 @@ | |||
<h1><%= has_modules ? message('update_key.fine_grained_key_update') : message('update_key.update_resource_key') -%></h1> | |||
<br/> | |||
<table id="project-history" class="data" style="width:1%"> | |||
<table class="data" style="width:1%"> | |||
<thead> | |||
<tr> | |||
<th><%= message('update_key.old_key') -%></th> | |||
@@ -89,7 +62,4 @@ | |||
</tbody> | |||
</table> | |||
<% | |||
end | |||
end | |||
%> | |||
<% end %> |
@@ -0,0 +1,49 @@ | |||
<% | |||
if @old_prefix && @new_prefix | |||
# validation screen for bulk update | |||
reset_cycle 'modules_tree' | |||
%> | |||
<script type="text/javascript"> | |||
function update_launched() { | |||
$$('input.action').each(function(input) { | |||
input.disabled=true; | |||
}); | |||
} | |||
</script> | |||
<h1><%= message('update_key.bulk_update') -%></h1> | |||
<br/> | |||
<p> | |||
<%= @can_update ? message('update_key.keys_will_be_updated_as_follows') : message('update_key.cant_update_because_duplicate_keys', :params => [@old_prefix, @new_prefix]) -%> | |||
</p> | |||
<table class="data" style="width:1%; margin-top: 10px"> | |||
<thead> | |||
<tr> | |||
<th><%= message('update_key.old_key') -%></th> | |||
<th><%= message('update_key.new_key') -%></th> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
<%= render :partial => 'prepare_keys', :locals => {:current_module => @project, :module_depth => 0, :key_check_results => @key_check_results} -%> | |||
</tbody> | |||
</table> | |||
<% if @can_update %> | |||
<% form_tag( {:action => 'perform_key_bulk_update', :id => @project.id }) do -%> | |||
<input type="hidden" value="<%= @project.id -%>" name="id" id="project_id"> | |||
<input type="hidden" value="<%= @old_prefix -%>" name="old_prefix" id="old_prefix"> | |||
<input type="hidden" value="<%= @new_prefix -%>" name="new_prefix" id="new_prefix"> | |||
<br/> | |||
<%= submit_tag message('update_key.rename'), :id => 'bulk_update_button', :class => 'action', | |||
:onclick => "update_launched();$('loading_bulk_update').show();" %> | |||
<a href="<%= url_for :action => 'key', :id => @project.key -%>"><%= message('cancel') -%></a> | |||
<span class="loading" id="loading_bulk_update" style="display: none; padding: 3px 10px;"></span> | |||
<% end %> | |||
<% else %> | |||
<br/> | |||
<a href="<%= url_for :action => 'key', :id => @project.key -%>"><%= message('back') -%></a> | |||
<% end %> | |||
<% end %> |