| /******************************************************************************* |
| * Copyright (c) 2008-2010 Sonatype, Inc. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Sonatype, Inc. - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.m2e.refactoring.rename; |
| |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.emf.common.command.CompoundCommand; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| import org.eclipse.emf.edit.command.SetCommand; |
| import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; |
| import org.eclipse.ltk.core.refactoring.RefactoringStatus; |
| import org.eclipse.osgi.util.NLS; |
| |
| import org.eclipse.m2e.model.edit.pom.Model; |
| import org.eclipse.m2e.model.edit.pom.util.PomResourceImpl; |
| import org.eclipse.m2e.refactoring.AbstractPomRefactoring; |
| import org.eclipse.m2e.refactoring.Messages; |
| import org.eclipse.m2e.refactoring.PomVisitor; |
| import org.eclipse.m2e.refactoring.RefactoringModelResources; |
| import org.eclipse.m2e.refactoring.RefactoringModelResources.PropertyInfo; |
| |
| |
| /** |
| * Rename artifact refactoring implementation |
| * |
| * @author Anton Kraev |
| */ |
| @SuppressWarnings("rawtypes") |
| public class RenameRefactoring extends AbstractPomRefactoring { |
| |
| private static final String VERSION = "version"; //$NON-NLS-1$ |
| |
| private static final String GETVERSION = Messages.RenameRefactoring_1; |
| |
| private static final String ARTIFACT_ID = "artifactId"; //$NON-NLS-1$ |
| |
| private static final String GETARTIFACT_ID = "getArtifactId"; //$NON-NLS-1$ |
| |
| private static final String GROUP_ID = "groupId"; //$NON-NLS-1$ |
| |
| private static final String GETGROUP_ID = "getGroupId"; //$NON-NLS-1$ |
| |
| // this page contains new values |
| MavenRenameWizardPage page; |
| |
| // old values |
| String oldGroupId; |
| |
| String oldArtifactId; |
| |
| String oldVersion; |
| |
| public RenameRefactoring(IFile file, MavenRenameWizardPage page) { |
| super(file); |
| this.page = page; |
| } |
| |
| // gets element from effective model based on path |
| private Object getElement(Object root, Path path) { |
| if(path == null || path.path.size() == 0) { |
| return root; |
| } |
| |
| PathElement current = path.path.remove(0); |
| String getterName = "get" + current.element; //$NON-NLS-1$ |
| |
| try { |
| Method getter = root.getClass().getMethod(getterName); |
| root = getElement(getter.invoke(root), path); |
| if(root instanceof List) { |
| List children = (List) root; |
| for(int i = 0; i < children.size(); i++ ) { |
| Object child = children.get(i); |
| Method artifact = child.getClass().getMethod(GETARTIFACT_ID); |
| String artifactId = (String) artifact.invoke(child); |
| if(current.artifactId != null && !current.artifactId.equals(artifactId)) |
| continue; |
| |
| //found, names are correct |
| return getElement(child, path); |
| } |
| } else { |
| return getElement(root, path); |
| } |
| return null; |
| } catch(Exception ex) { |
| return null; |
| } |
| } |
| |
| /** |
| * Finds all potential matched objects in model |
| */ |
| private List<EObjectWithPath> scanModel(Model model, String groupId, String artifactId, String version, |
| boolean processRoot) { |
| List<EObjectWithPath> res = new ArrayList<>(); |
| Path path = new Path(); |
| if(processRoot) { |
| scanObject(path, model, groupId, artifactId, version, res); |
| } else { |
| scanChildren(path, model, groupId, artifactId, version, res); |
| } |
| return res; |
| } |
| |
| // add candidate objects with same artifactId |
| private List<EObjectWithPath> scanObject(Path current, EObject obj, String groupId, String artifactId, String version, |
| List<EObjectWithPath> res) { |
| if(scanFeature(obj, ARTIFACT_ID, artifactId)) { |
| // System.out.println("found object " + obj + " : " + current); |
| res.add(new EObjectWithPath(obj, current)); |
| } |
| scanChildren(current, obj, groupId, artifactId, version, res); |
| return res; |
| } |
| |
| private List<EObjectWithPath> scanChildren(Path current, EObject obj, String groupId, String artifactId, |
| String version, List<EObjectWithPath> res) { |
| Iterator<EObject> it = obj.eContents().iterator(); |
| while(it.hasNext()) { |
| obj = it.next(); |
| Path child = current.clone(); |
| String element = obj.eContainingFeature().getName(); |
| element = element.substring(0, 1).toUpperCase() + element.substring(1); |
| child.addElement(element, artifactId); |
| scanObject(child, obj, groupId, artifactId, version, res); |
| } |
| return res; |
| } |
| |
| private boolean scanFeature(EObject obj, String featureName, String value) { |
| //not searching on this |
| if(value == null) { |
| return false; |
| } |
| EStructuralFeature feature = obj.eClass().getEStructuralFeature(featureName); |
| if(feature == null) { |
| return false; |
| } |
| String val = obj.eGet(feature) == null ? null : obj.eGet(feature).toString(); |
| if(value.equals(val)) { |
| return true; |
| } |
| return false; |
| } |
| |
| private String getValue(EObject obj, String featureName) { |
| EStructuralFeature feature = obj.eClass().getEStructuralFeature(featureName); |
| if(feature == null) { |
| return null; |
| } |
| return obj.eGet(feature) == null ? null : obj.eGet(feature).toString(); |
| } |
| |
| @Override |
| public String getNewProjectName() { |
| return page.getRenameEclipseProject() ? page.getNewArtifactId() : null; |
| } |
| |
| /** |
| * Applies new values in model |
| * |
| * @param editingDomain |
| * @param renameProject |
| * @throws NoSuchMethodException |
| * @throws Exception |
| */ |
| public CompoundCommand applyModel(RefactoringModelResources model, String newGroupId, String newArtifactId, |
| String newVersion, boolean processRoot) throws Exception { |
| // find all affected objects in EMF model |
| List<EObjectWithPath> affected = scanModel(model.getTmpModel(), this.oldGroupId, this.oldArtifactId, |
| this.oldVersion, processRoot); |
| |
| // go through all affected objects, check in effective model |
| Iterator<EObjectWithPath> i = affected.iterator(); |
| CompoundCommand command = new CompoundCommand(); |
| while(i.hasNext()) { |
| EObjectWithPath obj = i.next(); |
| Object effectiveObj = getElement(model.getEffective(), obj.path.clone()); |
| if(effectiveObj == null) { |
| // System.out.println("cannot find effective for: " + obj.object); |
| continue; |
| } |
| Method method = effectiveObj.getClass().getMethod(GETVERSION); |
| String effectiveVersion = (String) method.invoke(effectiveObj); |
| method = effectiveObj.getClass().getMethod(GETGROUP_ID); |
| String effectiveGroupId = (String) method.invoke(effectiveObj); |
| // if version from effective POM is different from old version, skip it |
| if(this.oldVersion != null && !this.oldVersion.equals(effectiveVersion)) { |
| continue; |
| } |
| |
| // only set groupId if effective group id is the same as old group id |
| if(oldGroupId != null && oldGroupId.equals(effectiveGroupId)) |
| applyFeature(editingDomain, model, GROUP_ID, newGroupId, command, obj); |
| // set artifact id unconditionally |
| applyFeature(editingDomain, model, ARTIFACT_ID, newArtifactId, command, obj); |
| // only set version if effective version is the same (already checked by the above) |
| // and new version is not empty |
| if(!"".equals(newVersion)) { //$NON-NLS-1$ |
| applyFeature(editingDomain, model, VERSION, newVersion, command, obj); |
| } |
| } |
| |
| return command.isEmpty() ? null : command; |
| } |
| |
| // apply the value, considering properties |
| private void applyFeature(AdapterFactoryEditingDomain editingDomain, RefactoringModelResources model, String feature, |
| String newValue, CompoundCommand command, EObjectWithPath obj) { |
| PropertyInfo info = null; |
| String old = getValue(obj.object, feature); |
| if(old != null && old.startsWith("${")) { //$NON-NLS-1$ |
| // this is a property, go find it |
| String pName = old.substring(2); |
| pName = pName.substring(0, pName.length() - 1).trim(); |
| info = model.getProperties().get(pName); |
| } |
| if(info != null) |
| info.setNewValue(new SetCommand(editingDomain, info.getPair(), |
| info.getPair().eClass().getEStructuralFeature("value"), newValue)); //$NON-NLS-1$ |
| else |
| applyObject(editingDomain, command, obj.object, feature, newValue); |
| } |
| |
| private void applyObject(AdapterFactoryEditingDomain editingDomain, CompoundCommand command, EObject obj, |
| String featureName, String value) { |
| EStructuralFeature feature = obj.eClass().getEStructuralFeature(featureName); |
| if(feature == null) { |
| return; |
| } |
| Object old = obj.eGet(feature); |
| if(old == null || old.equals(value)) { |
| return; |
| } |
| command.append(new SetCommand(editingDomain, obj, feature, value)); |
| } |
| |
| @Override |
| public RefactoringStatus checkInitialConditions(IProgressMonitor pm) |
| throws CoreException, OperationCanceledException { |
| PomResourceImpl resource = AbstractPomRefactoring.loadResource(file); |
| try { |
| Model model = (Model) resource.getContents().get(0); |
| this.oldArtifactId = model.getArtifactId(); |
| this.oldGroupId = model.getGroupId(); |
| this.oldVersion = model.getVersion(); |
| } finally { |
| resource.unload(); |
| } |
| RefactoringStatus res = new RefactoringStatus(); |
| return res; |
| } |
| |
| @Override |
| public String getName() { |
| return Messages.RenameRefactoring_name; |
| } |
| |
| @Override |
| public PomVisitor getVisitor() { |
| return new PomVisitor() { |
| |
| @Override |
| public CompoundCommand applyChanges(RefactoringModelResources current, IProgressMonitor pm) throws Exception { |
| //process <project> element only for the refactored file itself |
| boolean processRoot = current.getPomFile().equals(file); |
| return RenameRefactoring.this.applyModel(current, page.getNewGroupId(), page.getNewArtifactId(), |
| page.getNewVersion(), processRoot); |
| } |
| }; |
| } |
| |
| static class Path { |
| List<PathElement> path = new ArrayList<>(); |
| |
| public void addElement(String element, String artifactId) { |
| path.add(new PathElement(element, artifactId)); |
| } |
| |
| @Override |
| public String toString() { |
| return path.toString(); |
| } |
| |
| @Override |
| public Path clone() { |
| Path res = new Path(); |
| res.path = new ArrayList<>(this.path); |
| return res; |
| } |
| } |
| |
| // path (built during traversal of EMF model, used to find in effective model) |
| static class PathElement { |
| String element; |
| |
| String artifactId; |
| |
| public PathElement(String element, String artifactId) { |
| this.element = element; |
| this.artifactId = artifactId; |
| } |
| |
| @Override |
| public String toString() { |
| return "/" + element + "[artifactId=" + artifactId + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| } |
| } |
| |
| static class EObjectWithPath { |
| public EObject object; |
| |
| public Path path; |
| |
| public EObjectWithPath(EObject object, Path path) { |
| this.object = object; |
| this.path = path; |
| } |
| } |
| |
| // XXX move stuff UP after implementing another refactoring |
| // after moving up, use this |
| interface ScanVisitor { |
| public boolean interested(EObject obj); |
| } |
| |
| @Override |
| public boolean scanAllArtifacts() { |
| return true; |
| } |
| |
| @Override |
| public String getTitle() { |
| return NLS.bind(Messages.RenameRefactoring_title, file.getParent().getName()); |
| } |
| |
| } |