+ allow to replace any string of the key, not only the beginning.
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
#------------------------------------------------------------------------------
*/
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;
/**
}
}
- 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 {
}
}
- 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.");
}
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
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");
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");
+ }
+
}
--- /dev/null
+<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>
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);
}
}
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
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
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')
--- /dev/null
+<%
+ 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 %>
\ No newline at end of file
<%
if controller.java_facade.getResourceTypeBooleanProperty(@project.qualifier, 'updatable_key')
has_modules = !@project.modules.empty?
+ reset_cycle 'modules_tree'
%>
<script type="text/javascript">
}
</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/>
<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>
</tbody>
</table>
-<%
- end
- end
-%>
\ No newline at end of file
+<% end %>
\ No newline at end of file
--- /dev/null
+<%
+ 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 %>
\ No newline at end of file