diff options
author | Pascal Rapicault | 2010-12-06 22:10:52 +0000 |
---|---|---|
committer | Pascal Rapicault | 2010-12-06 22:10:52 +0000 |
commit | fbb4f5902cade92cc1689e73209c60a6413a5ef7 (patch) | |
tree | ace0fb107bafe980fc14fd7f3429e5c9aac2d795 /org.eclipse.m2e.refactoring/src | |
parent | a9c878c2624b33d8c74062717bf75302f0ae1fa7 (diff) | |
download | m2e-core-fbb4f5902cade92cc1689e73209c60a6413a5ef7.tar.gz m2e-core-fbb4f5902cade92cc1689e73209c60a6413a5ef7.tar.xz m2e-core-fbb4f5902cade92cc1689e73209c60a6413a5ef7.zip |
Initial commit at Eclipse
Diffstat (limited to 'org.eclipse.m2e.refactoring/src')
18 files changed, 2127 insertions, 0 deletions
diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/AbstractPomRefactoring.java b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/AbstractPomRefactoring.java new file mode 100644 index 00000000..12882164 --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/AbstractPomRefactoring.java @@ -0,0 +1,321 @@ +/******************************************************************************* + * 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 v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.refactoring; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.maven.execution.MavenExecutionRequest; +import org.apache.maven.project.MavenProject; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.emf.common.command.BasicCommandStack; +import org.eclipse.emf.common.command.CompoundCommand; +import org.eclipse.emf.common.notify.impl.AdapterFactoryImpl; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; +import org.eclipse.emf.edit.provider.ComposedAdapterFactory; +import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory; +import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.internal.corext.refactoring.rename.RenameJavaProjectProcessor; +import org.eclipse.ltk.core.refactoring.Change; +import org.eclipse.ltk.core.refactoring.CompositeChange; +import org.eclipse.ltk.core.refactoring.Refactoring; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.ltk.core.refactoring.TextFileChange; +import org.eclipse.ltk.core.refactoring.participants.RenameRefactoring; +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.embedder.IMaven; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.core.project.MavenProjectManager; +import org.eclipse.m2e.model.edit.pom.Model; +import org.eclipse.m2e.model.edit.pom.PropertyElement; +import org.eclipse.m2e.refactoring.RefactoringModelResources.PropertyInfo; +import org.eclipse.m2e.refactoring.internal.Activator; +import org.eclipse.osgi.util.NLS; + + +/** + * Base class for all pom.xml refactorings in workspace + * + * @author Anton Kraev + */ +@SuppressWarnings("restriction") +public abstract class AbstractPomRefactoring extends Refactoring { + + protected static final String PROBLEMS_DURING_REFACTORING = Messages.AbstractPomRefactoring_error; + + // main file that is being refactored + protected IFile file; + + // maven plugin + protected MavenPlugin mavenPlugin; + + // editing domain + protected AdapterFactoryEditingDomain editingDomain; + + private HashMap<String, RefactoringModelResources> models; + + public AbstractPomRefactoring(IFile file) { + this.file = file; + + this.mavenPlugin = MavenPlugin.getDefault(); + + List<AdapterFactoryImpl> factories = new ArrayList<AdapterFactoryImpl>(); + factories.add(new ResourceItemProviderAdapterFactory()); + factories.add(new ReflectiveItemProviderAdapterFactory()); + + ComposedAdapterFactory adapterFactory = new ComposedAdapterFactory(factories); + BasicCommandStack commandStack = new BasicCommandStack(); + this.editingDomain = new AdapterFactoryEditingDomain(adapterFactory, // + commandStack, new HashMap<Resource, Boolean>()); + } + + // this gets actual refactoring visitor + public abstract PomVisitor getVisitor(); + + @Override + public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { + return new RefactoringStatus(); + } + + @Override + public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException { + CompositeChange res = new CompositeChange(getTitle()); + IMavenProjectFacade[] projects = mavenPlugin.getMavenProjectManager().getProjects(); + pm.beginTask(Messages.AbstractPomRefactoring_task, projects.length); + + models = new HashMap<String, RefactoringModelResources>(); + + try { + // load all models + // XXX: assumption: artifactId is unique within workspace + for(IMavenProjectFacade projectFacade : projects) { + // skip "other" projects if not requested + if(!scanAllArtifacts() && !projectFacade.getPom().equals(file)) { + continue; + } + + // skip closed projects + if(!projectFacade.getProject().isAccessible() || !projectFacade.getPom().isAccessible()) { + continue; + } + + loadModel(projectFacade, pm); + } + + // construct properties for all models + for(IMavenProjectFacade projectFacade : projects) { + RefactoringModelResources model = models.get(projectFacade.getArtifactKey().getArtifactId()); + if(model == null) { + continue; + } + + Map<String, PropertyInfo> properties = new HashMap<String, PropertyInfo>(); + + // find all workspace parents + List<RefactoringModelResources> workspaceParents = new ArrayList<RefactoringModelResources>(); + MavenProject current = model.getProject(); + // add itself + workspaceParents.add(model); + for(MavenProject parentProject = getParentProject(projectFacade, current, pm); parentProject != null;) { + String id = parentProject.getArtifactId(); + RefactoringModelResources parent = models.get(id); + if(parent != null) { + workspaceParents.add(parent); + } else { + break; + } + parentProject = getParentProject(projectFacade, parentProject, pm); + } + + //fill properties (from the root) + for(int i = workspaceParents.size() - 1; i >= 0; i-- ) { + RefactoringModelResources resource = workspaceParents.get(i); + EList<PropertyElement> props = resource.getTmpModel().getProperties(); + if(props == null) + continue; + Iterator<?> it = props.iterator(); + while(it.hasNext()) { + PropertyElement pair = (PropertyElement) it.next(); + String pName = pair.getName(); + PropertyInfo info = properties.get(pName); + if(info == null) { + info = new PropertyInfo(); + properties.put(pName, info); + } + info.setPair(pair); + info.setResource(resource); + } + } + + model.setProperties(properties); + } + + // calculate the list of affected models + for(String artifact : models.keySet()) { + RefactoringModelResources model = models.get(artifact); + model.setCommand(getVisitor().applyChanges(model, pm)); + } + + // process all refactored properties, creating more commands + for(String artifact : models.keySet()) { + RefactoringModelResources model = models.get(artifact); + + if(model.getProperties() == null) { + continue; + } + + for(String pName : model.getProperties().keySet()) { + PropertyInfo info = model.getProperties().get(pName); + if(info.getNewValue() != null) { + CompoundCommand command = info.getResource().getCommand(); + if(command == null) { + command = new CompoundCommand(); + info.getResource().setCommand(command); + } + command.append(info.getNewValue()); + } + } + } + + // process the file itself first + for(String artifact : models.keySet()) { + RefactoringModelResources model = models.get(artifact); + if(model.getPomFile().equals(file)) { + processCommand(model, res); + model.releaseAllResources(); + models.remove(artifact); + break; + } + } + + // process others + for(String artifact : models.keySet()) { + processCommand(models.get(artifact), res); + } + + // rename project if required + // TODO probably should copy relevant classes from internal packages + String newName = getNewProjectName(); + if(newName != null) { + RenameJavaProjectProcessor processor = new RenameJavaProjectProcessor(JavaCore.create(file.getProject())); + RenameRefactoring refactoring = new RenameRefactoring(processor); + processor.setNewElementName(newName); + RefactoringStatus tmp = new RefactoringStatus(); + tmp.merge(refactoring.checkInitialConditions(pm)); + if(!tmp.hasFatalError()) { + tmp.merge(refactoring.checkFinalConditions(pm)); + if(!tmp.hasFatalError()) { + res.add(refactoring.createChange(pm)); + } + } + } + } catch(final PomRefactoringException ex) { + return new Change() { + public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException, OperationCanceledException { + return RefactoringStatus.createFatalErrorStatus(ex.getStatus().getMessage()); + } + public Object getModifiedElement() { + return null; + } + public String getName() { + return ex.getStatus().getMessage(); + } + public void initializeValidationData(IProgressMonitor pm) { + } + public Change perform(IProgressMonitor pm) throws CoreException { + return null; + } + public boolean isEnabled() { + return false; + } + }; + } catch(Exception ex) { + throw new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, PROBLEMS_DURING_REFACTORING, ex)); + } finally { + for(String artifact : models.keySet()) { + models.get(artifact).releaseAllResources(); + } + RefactoringModelResources.cleanupTmpProject(); + } + + return res; + } + + protected MavenProject getParentProject(IMavenProjectFacade project, MavenProject current, IProgressMonitor monitor) + throws CoreException { + IMaven maven = mavenPlugin.getMaven(); + MavenProjectManager projectManager = mavenPlugin.getMavenProjectManager(); + + MavenExecutionRequest request = projectManager.createExecutionRequest(project.getPom(), + project.getResolverConfiguration(), monitor); + + return maven.resolveParentProject(request, current, monitor); + } + + // title for a composite change + public abstract String getTitle(); + + protected RefactoringModelResources loadModel(IMavenProjectFacade projectFacade, IProgressMonitor pm) + throws CoreException, IOException { + pm.setTaskName(NLS.bind(Messages.AbstractPomRefactoring_loading, projectFacade.getProject().getName())); + RefactoringModelResources current = new RefactoringModelResources(projectFacade); + models.put(current.effective.getArtifactId(), current); + pm.worked(1); + return current; + } + + // this method determines whether all artifacts will be sent to visitor or only main one + public abstract boolean scanAllArtifacts(); + + protected void processCommand(RefactoringModelResources model, CompositeChange res) throws Exception { + CompoundCommand command = model.getCommand(); + if(command == null) { + return; + } + if(command.canExecute()) { + // apply changes to temp file + editingDomain.getCommandStack().execute(command); + // create text change comparing temp file and real file + TextFileChange change = new ChangeCreator(model.getPomFile(), model.getPomBuffer().getDocument(), model + .getTmpBuffer().getDocument(), file.getParent().getName()).createChange(); + res.add(change); + } + } + + // returns new eclipse project name or null if no change + public String getNewProjectName() { + return null; + } + + public Model createModel() { + try { + Resource resource = MavenPlugin.getDefault().getMavenModelManager().loadResource(file); + return (Model) resource.getContents().get(0); + } catch(CoreException ex) { + MavenLogger.log(PROBLEMS_DURING_REFACTORING, ex); + return null; + } + } +} diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/ChangeCreator.java b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/ChangeCreator.java new file mode 100644 index 00000000..5a49dc99 --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/ChangeCreator.java @@ -0,0 +1,187 @@ +/******************************************************************************* + * 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 v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.refactoring; + +import java.util.ArrayList; +import java.util.Arrays; + +import org.eclipse.compare.rangedifferencer.IRangeComparator; +import org.eclipse.compare.rangedifferencer.RangeDifference; +import org.eclipse.compare.rangedifferencer.RangeDifferencer; +import org.eclipse.core.resources.IFile; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.ltk.core.refactoring.TextFileChange; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.text.edits.DeleteEdit; +import org.eclipse.text.edits.InsertEdit; +import org.eclipse.text.edits.MultiTextEdit; +import org.eclipse.text.edits.ReplaceEdit; +import org.eclipse.text.edits.TextEdit; +import org.eclipse.text.edits.TextEditGroup; + + +/** + * This class creates an org.eclipse.ltk.core.refactoring.DocumentChange instance based on old and new text values + * + * @author Anton Kraev + */ +public class ChangeCreator { + private String label; + + private IDocument oldDocument; + + private IDocument newDocument; + + private IFile oldFile; + + public ChangeCreator(IFile oldFile, IDocument oldDocument, IDocument newDocument, String label) { + this.newDocument = newDocument; + this.oldDocument = oldDocument; + this.oldFile = oldFile; + this.label = label; + } + + public TextFileChange createChange() throws Exception { + TextFileChange change = new TextFileChange(label, oldFile); + // change.setSaveMode(TextFileChange.FORCE_SAVE); + change.setEdit(new MultiTextEdit()); + Object leftSide = new LineComparator(oldDocument); + Object rightSide = new LineComparator(newDocument); + + RangeDifference[] differences = RangeDifferencer.findDifferences((IRangeComparator) leftSide, (IRangeComparator) rightSide); + int insertOffset = 0; + for(int i = 0; i < differences.length; i++ ) { + RangeDifference curr = differences[i]; + int startLine = 0; + // when comparing 2 files, only RangeDifference.CHANGE is possible, no need to test + if (curr.rightLength() == curr.leftLength()) { + // replace + startLine = curr.rightStart(); + int endLine = curr.rightEnd() - 1; + for(int j = startLine; j <= endLine; j++ ) { + int newPos = curr.leftStart() - startLine + j; + String newText = newDocument.get(newDocument.getLineOffset(newPos), newDocument.getLineLength(newPos)); + addEdit(change, startLine, new ReplaceEdit(oldDocument.getLineOffset(j), oldDocument.getLineLength(j), newText)); + } + } else if (curr.rightLength() > 0 && curr.leftLength() == 0) { + // insert + startLine = curr.rightStart(); + int endLine = curr.rightEnd() - 1; + int posInsert = oldDocument.getLineOffset(curr.leftStart()); + String newText = ""; //$NON-NLS-1$ + for(int j = startLine; j <= endLine; j++ ) { + int newPos = curr.leftStart() - startLine + j + insertOffset; + newText += newDocument.get(newDocument.getLineOffset(newPos), newDocument.getLineLength(newPos)); + } + if(newText.length() > 0){ + addEdit(change, startLine, new InsertEdit(posInsert, newText)); + } + insertOffset += curr.rightEnd() - curr.rightStart(); + } else if (curr.leftLength() > 0 && curr.rightLength() == 0) { + // delete + startLine = curr.leftStart(); + int endLine = curr.leftEnd() - 1; + int startOffset = oldDocument.getLineOffset(startLine); + int endOffset = 0; + for(int j = startLine; j <= endLine; j++ ) { + endOffset += oldDocument.getLineLength(j); + } + addEdit(change, startLine, new DeleteEdit(startOffset, endOffset)); + insertOffset -= (curr.leftEnd() - curr.leftStart()); + } else { + // unhandled + } + } + return change; + } + + private void addEdit(TextFileChange change, int startLine, TextEdit edit) { + change.addTextEditGroup(new TextEditGroup("Line " + (startLine + 1), edit)); + change.addEdit(edit); + } + + public static class LineComparator implements IRangeComparator { + private final IDocument document; + private final ArrayList<Integer> hashes; + + /** + * Create a line comparator for the given document. + * + * @param document + */ + public LineComparator(IDocument document) { + this.document = document; + this.hashes = new ArrayList<Integer>(Arrays.asList(new Integer[document.getNumberOfLines()])); + } + + /* + * @see org.eclipse.compare.rangedifferencer.IRangeComparator#getRangeCount() + */ + public int getRangeCount() { + return document.getNumberOfLines(); + } + + /* + * @see org.eclipse.compare.rangedifferencer.IRangeComparator#rangesEqual(int, org.eclipse.compare.rangedifferencer.IRangeComparator, int) + */ + public boolean rangesEqual(int thisIndex, IRangeComparator other, int otherIndex) { + try { + return getHash(thisIndex).equals(((LineComparator) other).getHash(otherIndex)); + } catch (BadLocationException e) { + MavenLogger.log("Problem comparing", e); + return false; + } + } + + /* + * @see org.eclipse.compare.rangedifferencer.IRangeComparator#skipRangeComparison(int, int, org.eclipse.compare.rangedifferencer.IRangeComparator) + */ + public boolean skipRangeComparison(int length, int maxLength, IRangeComparator other) { + return false; + } + + /** + * @param line the number of the line in the document to get the hash for + * @return the hash of the line + * @throws BadLocationException if the line number is invalid + */ + private Integer getHash(int line) throws BadLocationException { + Integer hash = hashes.get(line); + if (hash == null) { + IRegion lineRegion; + lineRegion = document.getLineInformation(line); + String lineContents= document.get(lineRegion.getOffset(), lineRegion.getLength()); + hash = new Integer(computeDJBHash(lineContents)); + hashes.set(line, hash); + } + return hash; + } + + /** + * Compute a hash using the DJB hash algorithm + * + * @param string the string for which to compute a hash + * @return the DJB hash value of the string + */ + private int computeDJBHash(String string) { + int hash = 5381; + int len = string.length(); + for (int i = 0; i < len; i++) { + hash = (hash << 5) + hash + string.charAt(i); + } + return hash; + } + } + +} diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/Messages.java b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/Messages.java new file mode 100644 index 00000000..6dfe7d59 --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/Messages.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * 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 v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.refactoring; + +import org.eclipse.osgi.util.NLS; + + +/** + * @author mkleint + * + */ +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.m2e.refactoring.messages"; //$NON-NLS-1$ + + public static String AbstractPomRefactoring_error; + + public static String AbstractPomRefactoring_loading; + + public static String AbstractPomRefactoring_task; + public static String ExcludeRefactoring_error_parent; + + public static String ExcludeRefactoring_name; + + public static String ExcludeRefactoring_task_loading; + + public static String ExcludeRefactoring_title; + + public static String MavenRenameWizardPage_cbRenameWorkspace; + + public static String MavenRenameWizardPage_desc; + + public static String MavenRenameWizardPage_lblArtifactId; + + public static String MavenRenameWizardPage_lblGroupId; + + public static String MavenRenameWizardPage_lblVersion; + + public static String MavenRenameWizardPage_title; + + public static String RefactoringMavenMenuCreator_action_exclude; + + public static String RenameRefactoring_1; + + public static String RenameRefactoring_name; + + public static String RenameRefactoring_title; + + public static String SaveDirtyFilesDialog_message_not_saved; + public static String SaveDirtyFilesDialog_title; + + public static String SaveDirtyFilesDialog_title_error; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/PomRefactoringException.java b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/PomRefactoringException.java new file mode 100644 index 00000000..75385c31 --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/PomRefactoringException.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * 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 v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.refactoring; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; + + +/** + * A POM refactoring exception, to pass error from refactoring implementations into the wizard. + */ +public class PomRefactoringException extends CoreException { + private static final long serialVersionUID = 994564746763321105L; + + public PomRefactoringException(IStatus status) { + super(status); + } + +} diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/PomVisitor.java b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/PomVisitor.java new file mode 100644 index 00000000..7d01169e --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/PomVisitor.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * 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 v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.refactoring; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.emf.common.command.CompoundCommand; + + +/** + * This interface defines refactoring visitor + * + * @author Anton Kraev + */ +public interface PomVisitor { + /** + * Applies refactoring changes through undoable command + * + * @param model - current model being visited + * @param pm - progress monitor + * @return command that executes changes (if any) + * @throws Exception + */ + public CompoundCommand applyChanges(RefactoringModelResources model, IProgressMonitor pm) throws Exception; +} diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/RefactoringModelResources.java b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/RefactoringModelResources.java new file mode 100644 index 00000000..c47bd0d8 --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/RefactoringModelResources.java @@ -0,0 +1,194 @@ +/******************************************************************************* + * 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 v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.refactoring; + +import java.io.File; +import java.io.IOException; +import java.util.Map; + +import org.apache.maven.project.MavenProject; +import org.eclipse.core.filebuffers.FileBuffers; +import org.eclipse.core.filebuffers.ITextFileBuffer; +import org.eclipse.core.filebuffers.ITextFileBufferManager; +import org.eclipse.core.filebuffers.LocationKind; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.common.command.CompoundCommand; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.core.project.IMavenProjectFacade; +import org.eclipse.m2e.model.edit.pom.Model; +import org.eclipse.m2e.model.edit.pom.PropertyElement; + +/** + * This class manages all refactoring-related resources for a particular maven project + * + * @author Anton Kraev + */ +public class RefactoringModelResources { + private static final String TMP_PROJECT_NAME = ".m2eclipse_refactoring"; //$NON-NLS-1$ + protected IFile pomFile; + protected IFile tmpFile; + protected ITextFileBuffer pomBuffer; + protected ITextFileBuffer tmpBuffer; + protected Model tmpModel; + protected org.apache.maven.model.Model effective; + protected ITextFileBufferManager textFileBufferManager; + protected Map<String, PropertyInfo> properties; + protected MavenProject project; + protected CompoundCommand command; + protected static IProject tmpProject; + + protected IProject getTmpProject() { + if (tmpProject == null) { + tmpProject = ResourcesPlugin.getWorkspace().getRoot().getProject(TMP_PROJECT_NAME); + } + if (!tmpProject.exists()) { + try { + tmpProject.create(null); + tmpProject.open(null); + } catch(CoreException ex) { + MavenLogger.log(ex); + } + } + return tmpProject; + } + + public RefactoringModelResources(IMavenProjectFacade projectFacade) throws CoreException, IOException { + textFileBufferManager = FileBuffers.getTextFileBufferManager(); + project = projectFacade.getMavenProject(null); + effective = project.getModel(); + pomFile = projectFacade.getPom(); + pomBuffer = getBuffer(pomFile); + + //create temp file + IProject project = getTmpProject(); + File f = File.createTempFile("pom", ".xml", project.getLocation().toFile()); //$NON-NLS-1$ //$NON-NLS-2$ + f.delete(); + tmpFile = project.getFile(f.getName()); + pomFile.copy(tmpFile.getFullPath(), true, null); + + Resource resource = MavenPlugin.getDefault().getMavenModelManager().loadResource(tmpFile); + tmpModel = (Model)resource.getContents().get(0); + tmpBuffer = getBuffer(tmpFile); + } + + public CompoundCommand getCommand() { + return command; + } + + public void setCommand(CompoundCommand command) { + this.command = command; + } + + public IFile getPomFile() { + return pomFile; + } + + public IFile getTmpFile() { + return tmpFile; + } + + public ITextFileBuffer getPomBuffer() { + return pomBuffer; + } + + public ITextFileBuffer getTmpBuffer() { + return tmpBuffer; + } + + public Model getTmpModel() { + return tmpModel; + } + + public org.apache.maven.model.Model getEffective() { + return effective; + } + + public MavenProject getProject() { + return project; + } + + public Map<String, PropertyInfo> getProperties() { + return properties; + } + + public void setProperties(Map<String, PropertyInfo> properties) { + this.properties = properties; + } + + public void releaseAllResources() throws CoreException { + releaseBuffer(pomBuffer, pomFile); + if (tmpFile != null && tmpFile.exists()) { + releaseBuffer(tmpBuffer, tmpFile); + } + if (tmpModel != null) { + tmpModel.eResource().unload(); + } + } + + public static void cleanupTmpProject() throws CoreException { + if (tmpProject.exists()) { + tmpProject.delete(true, true, null); + } + } + + + protected ITextFileBuffer getBuffer(IFile file) throws CoreException { + textFileBufferManager.connect(file.getLocation(), LocationKind.NORMALIZE, null); + return textFileBufferManager.getTextFileBuffer(file.getLocation(), LocationKind.NORMALIZE); + } + + protected void releaseBuffer(ITextFileBuffer buffer, IFile file) throws CoreException { + buffer.revert(null); + textFileBufferManager.disconnect(file.getLocation(), LocationKind.NORMALIZE, null); + } + + public String getName() { + return pomFile.getProject().getName(); + } + + public static class PropertyInfo { + protected PropertyElement pair; + protected RefactoringModelResources resource; + protected Command newValue; + + public Command getNewValue() { + return newValue; + } + + public void setNewValue(Command newValue) { + this.newValue = newValue; + } + + public PropertyElement getPair() { + return pair; + } + + public void setPair(PropertyElement pair) { + this.pair = pair; + } + + public RefactoringModelResources getResource() { + return resource; + } + + public void setResource(RefactoringModelResources resource) { + this.resource = resource; + } + } + +} diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/exclude/DependencyExcludeAction.java b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/exclude/DependencyExcludeAction.java new file mode 100644 index 00000000..54eaf0e9 --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/exclude/DependencyExcludeAction.java @@ -0,0 +1,112 @@ +/******************************************************************************* + * 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 v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.refactoring.exclude; + +import org.apache.maven.artifact.Artifact; +import org.eclipse.core.resources.IFile; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.internal.ui.packageview.ClassPathContainer.RequiredProjectWrapper; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation; +import org.eclipse.m2e.core.actions.SelectionUtil; +import org.eclipse.m2e.core.embedder.ArtifactKey; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IActionDelegate; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IFileEditorInput; +import org.eclipse.ui.PlatformUI; + +/** + * This action is intended to be used in popup menus + * + * @author Anton Kraev + */ +@SuppressWarnings("restriction") +public class DependencyExcludeAction implements IActionDelegate { + + public static final String ID = "org.eclipse.m2e.refactoring.DependencyExclude"; //$NON-NLS-1$ + + private IFile file; + private ArtifactKey artifactKey; + + public void run(IAction action) { + if (artifactKey != null && file != null) { + Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); + MavenExcludeWizard wizard = new MavenExcludeWizard(file, // + artifactKey.getGroupId(), artifactKey.getArtifactId()); + try { + String titleForFailedChecks = ""; //$NON-NLS-1$ + RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard); + op.run(shell, titleForFailedChecks); + } catch(InterruptedException e) { + // XXX + } + } + } + + public void selectionChanged(IAction action, ISelection selection) { + file = null; + artifactKey = null; + + // TODO move logic into adapters + if (selection instanceof IStructuredSelection) { + IStructuredSelection structuredSelection = (IStructuredSelection) selection; + if(structuredSelection.size()==1) { + Object selected = structuredSelection.getFirstElement(); + if (selected instanceof Artifact) { + file = getFileFromEditor(); + artifactKey = new ArtifactKey((Artifact) selected); + + } else if (selected instanceof org.sonatype.aether.graph.DependencyNode) { + file = getFileFromEditor(); + artifactKey = new ArtifactKey(((org.sonatype.aether.graph.DependencyNode) selected).getDependency().getArtifact()); + + } else if (selected instanceof RequiredProjectWrapper) { + RequiredProjectWrapper w = (RequiredProjectWrapper) selected; + file = getFileFromProject(w.getParentClassPathContainer().getJavaProject()); + artifactKey = SelectionUtil.getType(selected, ArtifactKey.class); + + } else { + artifactKey = SelectionUtil.getType(selected, ArtifactKey.class); + if (selected instanceof IJavaElement) { + IJavaElement el = (IJavaElement) selected; + file = getFileFromProject(el.getParent().getJavaProject()); + } + + } + } + } + + if (artifactKey != null && file != null) { + action.setEnabled(true); + } else { + action.setEnabled(false); + } + } + + private IFile getFileFromProject(IJavaProject javaProject) { + return javaProject.getProject().getFile("pom.xml"); //$NON-NLS-1$ + } + + private IFile getFileFromEditor() { + IEditorPart part = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor(); + if (part != null && part.getEditorInput() instanceof IFileEditorInput) { + IFileEditorInput input = (IFileEditorInput) part.getEditorInput(); + return input.getFile(); + } + return null; + } + +} diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/exclude/ExcludeRefactoring.java b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/exclude/ExcludeRefactoring.java new file mode 100644 index 00000000..a0524178 --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/exclude/ExcludeRefactoring.java @@ -0,0 +1,195 @@ +/******************************************************************************* + * 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 v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.refactoring.exclude; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.emf.common.command.CompoundCommand; +import org.eclipse.emf.edit.command.AddCommand; +import org.eclipse.emf.edit.command.RemoveCommand; +import org.eclipse.ltk.core.refactoring.RefactoringStatus; +import org.eclipse.m2e.core.MavenPlugin; +import org.eclipse.m2e.core.core.IMavenConstants; +import org.eclipse.m2e.core.embedder.MavenModelManager; +import org.eclipse.m2e.model.edit.pom.Dependency; +import org.eclipse.m2e.model.edit.pom.Exclusion; +import org.eclipse.m2e.model.edit.pom.Model; +import org.eclipse.m2e.model.edit.pom.impl.PomFactoryImpl; +import org.eclipse.m2e.refactoring.AbstractPomRefactoring; +import org.eclipse.m2e.refactoring.Messages; +import org.eclipse.m2e.refactoring.PomRefactoringException; +import org.eclipse.m2e.refactoring.PomVisitor; +import org.eclipse.m2e.refactoring.RefactoringModelResources; +import org.eclipse.osgi.util.NLS; +import org.sonatype.aether.artifact.Artifact; +import org.sonatype.aether.graph.DependencyNode; +import org.sonatype.aether.graph.DependencyVisitor; +import org.sonatype.aether.util.artifact.JavaScopes; + + +/** + * Exclude artifact refactoring implementation + * + * @author Anton Kraev + */ +public class ExcludeRefactoring extends AbstractPomRefactoring { + + private String excludedArtifactId; + + private String excludedGroupId; + + /** + * @param file + */ + public ExcludeRefactoring(IFile file, String excludedGroupId, String excludedArtifactId) { + super(file); + this.excludedGroupId = excludedGroupId; + this.excludedArtifactId = excludedArtifactId; + } + + public PomVisitor getVisitor() { + return new PomVisitor() { + + public CompoundCommand applyChanges(RefactoringModelResources resources, IProgressMonitor pm) + throws CoreException, IOException { + final CompoundCommand command = new CompoundCommand(); + + final List<Dependency> toRemove = new ArrayList<Dependency>(); + + Model model = resources.getTmpModel(); + + final List<Dependency> deps = model.getDependencies(); + + final IStatus[] status = new IStatus[] {null}; + + pm.beginTask(Messages.ExcludeRefactoring_task_loading, 1); + MavenModelManager modelManager = MavenPlugin.getDefault().getMavenModelManager(); + DependencyNode root = modelManager.readDependencyTree(resources.getPomFile(), JavaScopes.TEST, pm); + pm.worked(1); + root.accept(new DependencyVisitor() { + + private Dependency findDependency(String groupId, String artifactId) { + for(Dependency d : deps) { + if(d.getGroupId().equals(groupId) && d.getArtifactId().equals(artifactId)) { + return d; + } + } + return null; + } + + private Dependency findDependency(DependencyNode node) { + Artifact artifact; + if(node.getRelocations().isEmpty()) { + artifact = node.getDependency().getArtifact(); + } else { + artifact = node.getRelocations().get(0); + } + return findDependency(artifact.getGroupId(), artifact.getArtifactId()); + } + + private int depth; + + private DependencyNode topLevel; + + private Set<Dependency> excluded = new HashSet<Dependency>(); + + public boolean visitLeave(DependencyNode node) { + depth-- ; + return status[0] == null; + } + + public boolean visitEnter(DependencyNode node) { + if(depth == 1) { + topLevel = node; + } + depth++ ; + + if(node.getDependency() != null) { + Artifact a = node.getDependency().getArtifact(); + if(a.getGroupId().equals(excludedGroupId) && a.getArtifactId().equals(excludedArtifactId)) { + if(topLevel == null) { + // do not touch itself + } else if(node == topLevel) { + // need to remove top-level dependency + toRemove.add(findDependency(topLevel)); + } else { + // need to add exclusion to top-level dependency + Dependency dependency = findDependency(topLevel); + if(dependency == null) { + status[0] = new Status(IStatus.ERROR, IMavenConstants.PLUGIN_ID, NLS.bind(Messages.ExcludeRefactoring_error_parent, + topLevel.getDependency().getArtifact().getGroupId(), + topLevel.getDependency().getArtifact().getArtifactId())); + } + if(excluded.add(dependency)) { + addExclusion(command, dependency); + } + } + return false; + } + } + + return true; + } + + }); + + if(status[0] != null) { + throw new PomRefactoringException(status[0]); + } + + for(Iterator<Dependency> rem = toRemove.iterator(); rem.hasNext();) { + command.append(new RemoveCommand(editingDomain, model.getDependencies(), rem.next())); + } + + // XXX scan management as well + + return command; + } + + private void addExclusion(CompoundCommand command, Dependency dep) { + Exclusion exclusion = PomFactoryImpl.eINSTANCE.createExclusion(); + exclusion.setArtifactId(excludedArtifactId); + exclusion.setGroupId(excludedGroupId); + command.append(new AddCommand(editingDomain, dep.getExclusions(), exclusion)); + } + }; + } + + public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException { + return new RefactoringStatus(); + } + + public String getName() { + return Messages.ExcludeRefactoring_name; + } + + public String getTitle() { + return NLS.bind(Messages.ExcludeRefactoring_title, new Object[] {excludedGroupId, excludedArtifactId, file.getParent().getName()}); + } + + public boolean scanAllArtifacts() { + //do not scan other artifacts + return false; + } + +} diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/exclude/MavenExcludeWizard.java b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/exclude/MavenExcludeWizard.java new file mode 100644 index 00000000..8bfd857a --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/exclude/MavenExcludeWizard.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * 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 v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.refactoring.exclude; + +import org.eclipse.core.resources.IFile; +import org.eclipse.ltk.ui.refactoring.RefactoringWizard; + + +/** + * @author Anton Kraev + */ +public class MavenExcludeWizard extends RefactoringWizard { + + public MavenExcludeWizard(IFile file, String excludedGroupId, String excludedArtifactId) { + super(new ExcludeRefactoring(file, excludedGroupId, excludedArtifactId), DIALOG_BASED_USER_INTERFACE); + } + + @Override + protected void addUserInputPages() { + setDefaultPageTitle(getRefactoring().getName()); + } + +} diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/internal/Activator.java b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/internal/Activator.java new file mode 100644 index 00000000..060a6f99 --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/internal/Activator.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * 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 v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.refactoring.internal; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * The activator class controls the plug-in life cycle + * + * @author Anton Kraev + */ +public class Activator extends AbstractUIPlugin { + + // The plug-in ID + public static final String PLUGIN_ID = "org.eclipse.m2e.refactoring"; //$NON-NLS-1$ + + // The shared instance + private static Activator plugin; + + /** + * The constructor + */ + public Activator() { + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#start(org.osgi.framework.BundleContext) + */ + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.plugin.AbstractUIPlugin#stop(org.osgi.framework.BundleContext) + */ + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static Activator getDefault() { + return plugin; + } + +} diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/internal/RefactoringImages.java b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/internal/RefactoringImages.java new file mode 100644 index 00000000..949c5ef8 --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/internal/RefactoringImages.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * 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 v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.refactoring.internal; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.ImageRegistry; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.ui.plugin.AbstractUIPlugin; + +/** + * @author Eugene Kuleshov + */ +public class RefactoringImages { + + // images + + // public static final Image IMG_CLEAR = createImage("clear.gif"); + + // image descriptors + + public static final ImageDescriptor EXCLUDE = create("exclude.gif"); //$NON-NLS-1$ + + + private static ImageDescriptor create(String key) { + try { + ImageDescriptor imageDescriptor = createDescriptor(key); + ImageRegistry imageRegistry = getImageRegistry(); + if(imageRegistry!=null) { + imageRegistry.put(key, imageDescriptor); + } + return imageDescriptor; + } catch (Exception ex) { + MavenLogger.log(key, ex); + return null; + } + } + +// private static Image createImage(String key) { +// create(key); +// ImageRegistry imageRegistry = getImageRegistry(); +// return imageRegistry==null ? null : imageRegistry.get(key); +// } + + private static ImageRegistry getImageRegistry() { + Activator plugin = Activator.getDefault(); + return plugin==null ? null : plugin.getImageRegistry(); + } + + private static ImageDescriptor createDescriptor(String image) { + return AbstractUIPlugin.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "icons/" + image); //$NON-NLS-1$ + } + +} diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/internal/RefactoringMavenMenuCreator.java b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/internal/RefactoringMavenMenuCreator.java new file mode 100644 index 00000000..781599cd --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/internal/RefactoringMavenMenuCreator.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * 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 v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.refactoring.internal; + +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.m2e.core.actions.AbstractMavenMenuCreator; +import org.eclipse.m2e.core.actions.SelectionUtil; +import org.eclipse.m2e.refactoring.Messages; +import org.eclipse.m2e.refactoring.exclude.DependencyExcludeAction; + +/** + * @author Eugene Kuleshov + */ +public class RefactoringMavenMenuCreator extends AbstractMavenMenuCreator { + + public void createMenu(IMenuManager mgr) { + int selectionType = SelectionUtil.getSelectionType(selection); + if(selectionType == SelectionUtil.JAR_FILE) { + mgr.appendToGroup(OPEN, getAction(new DependencyExcludeAction(), // + DependencyExcludeAction.ID, // + Messages.RefactoringMavenMenuCreator_action_exclude, // + RefactoringImages.EXCLUDE)); + } + } + +} + diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/internal/SaveDirtyFilesDialog.java b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/internal/SaveDirtyFilesDialog.java new file mode 100644 index 00000000..ec01227e --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/internal/SaveDirtyFilesDialog.java @@ -0,0 +1,166 @@ +/******************************************************************************* + * 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 v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.refactoring.internal; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.window.Window; +import org.eclipse.m2e.refactoring.Messages; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.dialogs.ListDialog; + +/** + * Taken from org.eclipse.wst.common.ui.internal.dialogs + * + * A generic save files dialog. The bulk of the code for this dialog was taken + * from the JDT refactoring support in + * org.eclipse.jdt.internal.ui.refactoring.RefactoringSaveHelper. This class is + * a good candidate for reuse amoung components. + */ +public class SaveDirtyFilesDialog extends ListDialog { + public static final String ALL_MODIFIED_RESOURCES_MUST_BE_SAVED_BEFORE_THIS_OPERATION = Messages.SaveDirtyFilesDialog_message_not_saved; + + public static boolean saveDirtyFiles(String mask) + { + boolean result = true; + // TODO (cs) add support for save automatically + Shell shell = Display.getCurrent().getActiveShell(); + IEditorPart[] dirtyEditors = getDirtyEditors(mask); + if (dirtyEditors.length > 0) + { + result = false; + SaveDirtyFilesDialog saveDirtyFilesDialog = new SaveDirtyFilesDialog(shell); + saveDirtyFilesDialog.setInput(Arrays.asList(dirtyEditors)); + // Save all open editors. + if (saveDirtyFilesDialog.open() == Window.OK) + { + result = true; + int numDirtyEditors = dirtyEditors.length; + for (int i = 0; i < numDirtyEditors; i++) + { + dirtyEditors[i].doSave(null); + } + } + else + { + MessageDialog dlg = new MessageDialog(shell, Messages.SaveDirtyFilesDialog_title_error, null, + ALL_MODIFIED_RESOURCES_MUST_BE_SAVED_BEFORE_THIS_OPERATION, + MessageDialog.ERROR, new String[] {IDialogConstants.OK_LABEL}, 0); + dlg.open(); + } + } + return result; + } + + private static IEditorPart[] getDirtyEditors(String mask) + { + List<IEditorPart> result = new ArrayList<IEditorPart>(0); + IWorkbench workbench = PlatformUI.getWorkbench(); + IWorkbenchWindow[] windows = workbench.getWorkbenchWindows(); + for (int i = 0; i < windows.length; i++) { + IWorkbenchPage[] pages = windows[i].getPages(); + for (int x = 0; x < pages.length; x++) { + IEditorPart[] editors = pages[x].getDirtyEditors(); + for (int z = 0; z < editors.length; z++) { + IEditorPart ep = editors[z]; + if (ep.getTitle().indexOf(mask) > 0) { + result.add(ep); + } + } + } + } + return result.toArray(new IEditorPart[result.size()]); + } + + public SaveDirtyFilesDialog(Shell parent) + { + super(parent); + setTitle(Messages.SaveDirtyFilesDialog_title); + setAddCancelButton(true); + setLabelProvider(createDialogLabelProvider()); + setMessage(ALL_MODIFIED_RESOURCES_MUST_BE_SAVED_BEFORE_THIS_OPERATION); + setContentProvider(new ListContentProvider()); + } + + protected Control createDialogArea(Composite container) + { + Composite result = (Composite) super.createDialogArea(container); + // TODO... provide preference that supports 'always save' + return result; + } + + + private ILabelProvider createDialogLabelProvider() + { + return new LabelProvider() + { + public Image getImage(Object element) + { + return ((IEditorPart) element).getTitleImage(); + } + + public String getText(Object element) + { + return ((IEditorPart) element).getTitle(); + } + }; + } + + /** + * A specialized content provider to show a list of editor parts. + * This class has been copied from org.eclipse.jdt.internal.ui.viewsupport.ListContentProvider + * This class should be removed once a generic solution is made available. + */ + @SuppressWarnings("unchecked") + static class ListContentProvider implements IStructuredContentProvider { + List fContents; + + public Object[] getElements(Object input) { + if(fContents != null && fContents == input) + return fContents.toArray(); + return new Object[0]; + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + if(newInput instanceof List) { + fContents = (List) newInput; + } else { + fContents = null; + // we use a fixed set. + } + } + + public void dispose() { + } + + public boolean isDeleted(Object o) { + return fContents != null && !fContents.contains(o); + } + } +} diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/messages.properties b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/messages.properties new file mode 100644 index 00000000..6c4053d5 --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/messages.properties @@ -0,0 +1,20 @@ +AbstractPomRefactoring_error=Problems during refactoring +AbstractPomRefactoring_loading=Loading {0} +AbstractPomRefactoring_task=Refactoring +ExcludeRefactoring_error_parent=Parent dependency not found for {0}:{1} +ExcludeRefactoring_name=Exclude Maven Artifact +ExcludeRefactoring_task_loading=Loading dependency tree +ExcludeRefactoring_title=Excluding {0}:{1} from {2} +MavenRenameWizardPage_cbRenameWorkspace=&Rename Eclipse project in Workspace +MavenRenameWizardPage_desc=Specify new group Id, artifact Id or version +MavenRenameWizardPage_lblArtifactId=&Artifact Id: +MavenRenameWizardPage_lblGroupId=&Group Id: +MavenRenameWizardPage_lblVersion=&Version: +MavenRenameWizardPage_title=Rename Maven Artifact +RefactoringMavenMenuCreator_action_exclude=Exclude Maven artifact... +RenameRefactoring_1=getVersion +RenameRefactoring_name=Rename Maven Artifact +RenameRefactoring_title=Renaming {0} +SaveDirtyFilesDialog_message_not_saved=All modified resources must be saved before this operation. +SaveDirtyFilesDialog_title=Save All Modified Resources +SaveDirtyFilesDialog_title_error=Error diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/rename/MavenRenameWizard.java b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/rename/MavenRenameWizard.java new file mode 100644 index 00000000..401ce95b --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/rename/MavenRenameWizard.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * 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 v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.refactoring.rename; + +import org.eclipse.core.resources.IFile; +import org.eclipse.ltk.ui.refactoring.RefactoringWizard; +import org.eclipse.m2e.model.edit.pom.Model; +import org.eclipse.m2e.refactoring.AbstractPomRefactoring; + + +/** + * @author Anton Kraev + */ +public class MavenRenameWizard extends RefactoringWizard { + + private static MavenRenameWizardPage page1 = new MavenRenameWizardPage(); + + public MavenRenameWizard(IFile file) { + super(new RenameRefactoring(file, page1), DIALOG_BASED_USER_INTERFACE); + } + + @Override + protected void addUserInputPages() { + setDefaultPageTitle(getRefactoring().getName()); + addPage(page1); + Model model = ((AbstractPomRefactoring) getRefactoring()).createModel(); + page1.initialize(model.getGroupId(), model.getArtifactId(), model.getVersion()); + model.eResource().unload(); + } + +} diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/rename/MavenRenameWizardPage.java b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/rename/MavenRenameWizardPage.java new file mode 100644 index 00000000..19d47b00 --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/rename/MavenRenameWizardPage.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * 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 v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.refactoring.rename; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.ltk.ui.refactoring.UserInputWizardPage; +import org.eclipse.m2e.refactoring.Messages; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; + + +/** + * @author Anton Kraev + */ +public class MavenRenameWizardPage extends UserInputWizardPage { + private Text groupIdText; + private Text artifactIdText; + private Text versionText; + private Button renameCheckbox; + + private String groupId; + private String artifactId; + private String version; + private String newGroupId = ""; //$NON-NLS-1$ + private String newArtifactId = ""; //$NON-NLS-1$ + private String newVersion = ""; //$NON-NLS-1$ + private boolean renamed; + + protected MavenRenameWizardPage() { + super("MavenRenameWizardPage"); //$NON-NLS-1$ + setDescription(Messages.MavenRenameWizardPage_desc); + setTitle(Messages.MavenRenameWizardPage_title); + } + + public void initialize(String groupId, String artifactID, String version) { + this.groupId = newGroupId = nvl(groupId); + this.artifactId = newArtifactId = nvl(artifactID); + this.version = newVersion = nvl(version); + } + + public String getNewGroupId() { + return newGroupId; + } + + public String getNewArtifactId() { + return newArtifactId; + } + + public String getNewVersion() { + return newVersion; + } + + @Override + public boolean isPageComplete() { + boolean renamedArtifact = !newArtifactId.equals(artifactId); + renameCheckbox.setEnabled(renamedArtifact); + if (!renamedArtifact) { + renameCheckbox.setSelection(false); + renamed = false; + } + return !newGroupId.equals(groupId) // + || renamedArtifact // + || !newVersion.equals(version) // + || !isCurrentPage(); + } + + public void createControl(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + GridLayout gridLayout = new GridLayout(2, false); + gridLayout.marginWidth = 10; + gridLayout.marginHeight = 10; + composite.setLayout(gridLayout); + initializeDialogUnits(composite); + Dialog.applyDialogFont(composite); + setControl(composite); + + Label groupIdLabel = new Label(composite, SWT.NONE); + groupIdLabel.setLayoutData(new GridData()); + groupIdLabel.setText(Messages.MavenRenameWizardPage_lblGroupId); + + groupIdText = new Text(composite, SWT.BORDER); + groupIdText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + groupIdText.setData("name", "groupId"); //$NON-NLS-1$ //$NON-NLS-2$ + + Label artifactIdLabel = new Label(composite, SWT.NONE); + artifactIdLabel.setLayoutData(new GridData()); + artifactIdLabel.setText(Messages.MavenRenameWizardPage_lblArtifactId); + + artifactIdText = new Text(composite, SWT.BORDER); + artifactIdText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + artifactIdText.setData("name", "artifactId"); //$NON-NLS-1$ //$NON-NLS-2$ + + Label versionLabel = new Label(composite, SWT.NONE); + versionLabel.setLayoutData(new GridData()); + versionLabel.setText(Messages.MavenRenameWizardPage_lblVersion); + + versionText = new Text(composite, SWT.BORDER); + versionText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + versionText.setData("name", "version"); //$NON-NLS-1$ //$NON-NLS-2$ + + new Label(composite, SWT.NONE); + + renameCheckbox = new Button(composite, SWT.CHECK); + renameCheckbox.setText(Messages.MavenRenameWizardPage_cbRenameWorkspace); + renameCheckbox.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + renameCheckbox.setData("name", "rename"); //$NON-NLS-1$ //$NON-NLS-2$ + renameCheckbox.setEnabled(false); + renameCheckbox.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + renamed = renameCheckbox.getSelection(); + getWizard().getContainer().updateButtons(); + } + }); + + ModifyListener listener = new ModifyListener() { + public void modifyText(ModifyEvent e) { + newGroupId = groupIdText.getText(); + newArtifactId = artifactIdText.getText(); + newVersion = versionText.getText(); + getWizard().getContainer().updateButtons(); + } + }; + + groupIdText.setText(groupId); + artifactIdText.setText(artifactId); + versionText.setText(version); + + groupIdText.addModifyListener(listener); + artifactIdText.addModifyListener(listener); + versionText.addModifyListener(listener); + } + + private String nvl(String str) { + return str == null ? "" : str; //$NON-NLS-1$ + } + + public boolean getRenameEclipseProject() { + return renamed; + } + +} diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/rename/RenameArtifactAction.java b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/rename/RenameArtifactAction.java new file mode 100644 index 00000000..0556df63 --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/rename/RenameArtifactAction.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * 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 v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Sonatype, Inc. - initial API and implementation + *******************************************************************************/ + +package org.eclipse.m2e.refactoring.rename; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ltk.ui.refactoring.RefactoringWizardOpenOperation; +import org.eclipse.m2e.core.core.MavenLogger; +import org.eclipse.m2e.refactoring.internal.SaveDirtyFilesDialog; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.actions.ActionDelegate; +import org.eclipse.ui.internal.ObjectPluginAction; + + +/** + * @author Anton Kraev + */ +@SuppressWarnings("restriction") +public class RenameArtifactAction extends ActionDelegate { + + @Override + public void init(IAction action) { + super.init(action); + } + + @Override + public void run(IAction action) { + doRun(action); + } + + @Override + public void runWithEvent(IAction action, Event event) { + doRun(action); + } + + public void doRun(IAction action) { + Object element = ((IStructuredSelection) ((ObjectPluginAction) action).getSelection()).getFirstElement(); + if(element instanceof IFile) { + rename((IFile) element); + } else if (element instanceof IProject) { + IProject project = (IProject) element; + IFile file = project.getFile("pom.xml"); //$NON-NLS-1$ + if(file!=null) { + rename(file); + } + } + } + + private void rename(IFile file) { + try { + // get the model from existing file + Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); + boolean rc = SaveDirtyFilesDialog.saveDirtyFiles("pom.xml"); //$NON-NLS-1$ + if (!rc) + return; + MavenRenameWizard wizard = new MavenRenameWizard(file); + RefactoringWizardOpenOperation op = new RefactoringWizardOpenOperation(wizard); + String titleForFailedChecks = ""; //$NON-NLS-1$ + op.run(shell, titleForFailedChecks); + } catch(Exception e) { + MavenLogger.log("Unable to rename " + file, e); + } + } + +} + diff --git a/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/rename/RenameRefactoring.java b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/rename/RenameRefactoring.java new file mode 100644 index 00000000..a0e495d5 --- /dev/null +++ b/org.eclipse.m2e.refactoring/src/org/eclipse/m2e/refactoring/rename/RenameRefactoring.java @@ -0,0 +1,332 @@ +/******************************************************************************* + * 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 v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * 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.m2e.core.MavenPlugin; +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; +import org.eclipse.osgi.util.NLS; + + +/** + * Rename artifact refactoring implementation + * + * @author Anton Kraev + */ +@SuppressWarnings("unchecked") +public class RenameRefactoring extends AbstractPomRefactoring { + private static final Object[] EMPTY_OBJECT_ARRAY = new Object[] {}; + 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, new Class[] {}); + root = getElement(getter.invoke(root, EMPTY_OBJECT_ARRAY), 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, new Class[] {}); + String artifactId = (String) artifact.invoke(child, EMPTY_OBJECT_ARRAY); + 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<EObjectWithPath>(); + 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(); + } + + 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, new Class[] {}); + String effectiveVersion = (String) method.invoke(effectiveObj, EMPTY_OBJECT_ARRAY); + method = effectiveObj.getClass().getMethod(GETGROUP_ID, new Class[] {}); + String effectiveGroupId = (String) method.invoke(effectiveObj, EMPTY_OBJECT_ARRAY); + // 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 = MavenPlugin.getDefault().getMavenModelManager().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() { + + 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<PathElement>(); + + public void addElement(String element, String artifactId) { + path.add(new PathElement(element, artifactId)); + } + + public String toString() { + return path.toString(); + } + + public Path clone() { + Path res = new Path(); + res.path = new ArrayList<PathElement>(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; + } + + 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); + } + + public boolean scanAllArtifacts() { + return true; + } + + public String getTitle() { + return NLS.bind(Messages.RenameRefactoring_title, file.getParent().getName()); + } + +} |