/******************************************************************************* * Copyright (c) 2007, 2011 Spring IDE Developers * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Spring IDE Developers - initial API and implementation *******************************************************************************/ package org.springframework.ide.eclipse.beans.ui.refactoring.ltk; import java.io.IOException; import java.text.MessageFormat; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.ChangeDescriptor; import org.eclipse.ltk.core.refactoring.CompositeChange; import org.eclipse.ltk.core.refactoring.Refactoring; import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.eclipse.wst.sse.core.StructuredModelManager; import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel; import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; import org.springframework.ide.eclipse.beans.core.BeansCorePlugin; import org.springframework.ide.eclipse.beans.core.internal.model.BeansModelUtils; import org.springframework.ide.eclipse.beans.core.model.IBeansConfig; import org.springframework.ide.eclipse.beans.core.model.IBeansConfigSet; import org.springframework.ide.eclipse.beans.core.model.IBeansImport; import org.springframework.ide.eclipse.beans.core.model.IBeansProject; import org.springframework.ide.eclipse.beans.ui.editor.util.BeansEditorUtils; import org.springframework.ide.eclipse.beans.ui.refactoring.util.BeansRefactoringChangeUtils; import org.springframework.util.StringUtils; /** * @author Christian Dupuis * @author Torsten Juergeleit * @author Martin Lippert * @since 2.6.0 */ @SuppressWarnings("restriction") public class RenameIdRefactoring extends Refactoring { protected static final String ID_TYPE = "id-type"; protected static final String FILE = "file"; protected static final String NAME = "name"; protected static final String OFFSET = "offset"; protected static final String OLDNAME = "oldName"; protected static final String REFERENCES = "references"; private RenameIdType type; protected String beanId; protected IFile file; protected IDOMNode node; protected String oldBeanId; protected int offset; protected boolean updateReferences = true; public RenameIdRefactoring() { } @Override public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { try { pm.beginTask("Creating change...", 1); CompositeChange compositeChange = new CompositeChange("Rename Spring " + type.getType() + " id") { @Override public ChangeDescriptor getDescriptor() { String project = file.getProject().getName(); String description = MessageFormat.format("Rename Spring " + type.getType() + " ''{0}''", new Object[] { oldBeanId }); String comment = MessageFormat.format( "Rename Spring " + type.getType() + " from ''{0}'' to ''{1}''", new Object[] { oldBeanId, beanId }); Map<String, String> arguments = new HashMap<String, String>(); arguments.put(OLDNAME, oldBeanId); arguments.put(NAME, beanId); arguments.put(ID_TYPE, type.toString()); arguments.put(FILE, file.getFullPath().toString()); arguments.put(REFERENCES, Boolean.valueOf(updateReferences).toString()); arguments.put(OFFSET, Integer.toString(offset)); RenameIdRefactoringDescriptor idRefactoringDescriptor = new RenameIdRefactoringDescriptor(project, description, comment, arguments); return new RefactoringChangeDescriptor(idRefactoringDescriptor); } }; Change change = BeansRefactoringChangeUtils.createRenameBeanIdChange(file, type, oldBeanId, beanId, updateReferences, pm); if (change != null) { compositeChange.add(change); } if (updateReferences) { addChangesForUpdatedReferences(type, compositeChange, pm); } return compositeChange; } finally { pm.done(); } } private void addChangesForUpdatedReferences(RenameIdType descriptor, CompositeChange compositeChange, IProgressMonitor pm) throws CoreException { IBeansConfig config = BeansCorePlugin.getModel().getConfig(file); if (config != null) { Set<IBeansConfig> visitedResources = new HashSet<IBeansConfig>(); visitedResources.add(config); // rename id in imported configs for (IBeansImport import_ : config.getImports()) { for (IBeansConfig bc : import_.getImportedBeansConfigs()) { addChangesForUpdateReferences(bc, descriptor, compositeChange, visitedResources, pm); } } // rename id in configs that import this one IBeansConfig importingBeansConfig = BeansModelUtils.getImportingBeansConfig(config); if (importingBeansConfig != null) { addChangesForUpdateReferences(importingBeansConfig, descriptor, compositeChange, visitedResources, pm); } // rename id in bean config sets for (IBeansProject project : BeansCorePlugin.getModel().getProjects()) { for (IBeansConfigSet configSet : project.getConfigSets()) { if (configSet.getConfigs().contains(config)) { for (IBeansConfig bc : configSet.getConfigs()) { addChangesForUpdateReferences(bc, descriptor, compositeChange, visitedResources, pm); } } } } } } private void addChangesForUpdateReferences(IBeansConfig beansConfig, RenameIdType descriptor, CompositeChange compositeChange, Set<IBeansConfig> visitedResources, IProgressMonitor pm) throws CoreException { if (!visitedResources.contains(beansConfig) && !beansConfig.isElementArchived()) { IResource res = beansConfig.getElementResource(); if (res.isAccessible() && res instanceof IFile) { visitedResources.add(beansConfig); Change refsChange = BeansRefactoringChangeUtils .createRenameBeanRefsChange((IFile) beansConfig.getElementResource(), descriptor, oldBeanId, beanId, pm); if (refsChange != null) { compositeChange.add(refsChange); } } } } @Override public RefactoringStatus checkInitialConditions(IProgressMonitor monitor) throws CoreException, OperationCanceledException { RefactoringStatus status = new RefactoringStatus(); try { monitor.beginTask("Checking preconditions...", 1); if (file == null) { status.merge(RefactoringStatus.createFatalErrorStatus("Config file not given")); } else if (!file.exists()) { status.merge(RefactoringStatus.createFatalErrorStatus(MessageFormat.format( "File ''{0}'' is not a " + "Spring IDE Beans Config.", new Object[] { file .getFullPath().toString() }))); } if (node == null) { status.merge(RefactoringStatus.createFatalErrorStatus("Selection not given")); } else if (!BeansEditorUtils.hasAttribute(node, "id")) { status.merge(RefactoringStatus .createFatalErrorStatus("Selected XML element has no id")); } } finally { monitor.done(); } return status; } @Override public RefactoringStatus checkFinalConditions(IProgressMonitor monitor) throws CoreException, OperationCanceledException { RefactoringStatus status = new RefactoringStatus(); return status; } public RenameIdType getType() { return type; } public void setType(RenameIdType type) { this.type = type; } @Override public String getName() { return "Rename " + this.type; } public String getBeanId() { if (this.beanId == null) { return BeansEditorUtils.getAttribute(node, "id"); } else { return this.beanId; } } public RefactoringStatus setBeanId(String beanId) { this.beanId = beanId; RefactoringStatus status = new RefactoringStatus(); if (!StringUtils.hasText(beanId)) { status.merge(RefactoringStatus.createFatalErrorStatus(getType().getType() + " id cannot be empty")); } else if (this.node != null && this.node.getOwnerDocument().getElementById(beanId) != null) { status .merge(RefactoringStatus .createInfoStatus(getType().getType() + " id already used in current file")); } return status; } public void setFile(IFile file) { this.file = file; } public void setNode(IDOMNode node) { this.node = node; this.oldBeanId = BeansEditorUtils.getAttribute(node, "id"); } public void setOffset(int offset) { this.offset = offset; } public void setUpdateReferences(boolean updateReferences) { this.updateReferences = updateReferences; } public RefactoringStatus initialize(Map<String, String> arguments) { RefactoringStatus status = new RefactoringStatus(); String value = arguments.get(ID_TYPE); if (value != null) { setType(RenameIdType.valueOf(value)); } value = arguments.get(REFERENCES); if (value != null) { setUpdateReferences(Boolean.valueOf(value).booleanValue()); } value = arguments.get(FILE); if (value != null) { IContainer container = ResourcesPlugin.getWorkspace().getRoot(); IResource resource = container.findMember(value); if (resource == null || !(resource instanceof IFile)) { status.merge(RefactoringStatus.createErrorStatus("Cannot get file")); } else { setFile((IFile) resource); } } value = arguments.get(NAME); if (value != null) { setBeanId(value); } value = arguments.get(OLDNAME); if (value != null) { this.oldBeanId = value; } value = arguments.get(OFFSET); if (value != null) { int offset = Integer.valueOf(value); IStructuredModel model = null; try { model = StructuredModelManager.getModelManager().getModelForRead(this.file); IndexedRegion inode = model.getIndexedRegion(offset); if (inode == null) { inode = model.getIndexedRegion(offset - 1); } if (inode instanceof IDOMNode) { this.node = (IDOMNode) inode; } } catch (IOException e) { } catch (CoreException e) { } finally { if (model != null) { model.releaseFromRead(); } } } return status; } }