Skip to content

Commit

Permalink
Show Maven artifacts via list instead of tabs for the dependency edit…
Browse files Browse the repository at this point in the history
…or. (#1157)

The current implementation can only show ~5 items at a time. All
remaining dependencies have to be selected via a combo box. For projects
with several dozen dependencies, it is often faster to edit the XML file
directly, rather than using this wizard.

The new implementation keeps all dependencies in a table. Items can be
sorted by column and arbitrary elements can be added or removed. Support
has been added to update a selected number of artifacts to their latest
version. Undo/Redo functionality has been added.

A simple validation is added to check that all required attributes of a
dependency are set. Meaning group id, artifact id, version and type.
  • Loading branch information
ptziegler authored Jan 19, 2023
1 parent 2ca5d4b commit 19a4aba
Show file tree
Hide file tree
Showing 12 changed files with 1,101 additions and 211 deletions.
6 changes: 6 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ This often lead to a poor user-experience and we think that users are adding moj
Because of this, m2e now automatically enables the execution of mojos if there is no mapping configured, in case you want to change this there is a new configuration option to control the behavior:
![grafik](https://user-images.githubusercontent.com/1331477/211298610-0fa92418-246a-4377-913a-60d02d63013b.png)

### Updated Dependency Editor

The dependencies editor has been adapted to show all artifacts within a target location as a single table, instead of multiple tabs. This change also includes support for only updating a selected number of artifacts to their latest version, as well as a undo/redo functionality.

![grafik](https://user-images.githubusercontent.com/70652978/212153011-160fa96a-1c06-4092-9b89-fcd7a3c2859e.png)

## 2.1.0

* 📅 Release Date: November 24th 2022
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -396,33 +396,11 @@ public MavenTargetLocation update(IProgressMonitor monitor) throws CoreException
List<MavenTargetDependency> latest = new ArrayList<>();
int updated = 0;
for (MavenTargetDependency dependency : roots) {
Artifact artifact = new DefaultArtifact(
dependency.getGroupId() + ":" + dependency.getArtifactId() + ":(0,]");
IMaven maven = MavenPlugin.getMaven();
RepositorySystem repoSystem = MavenPluginActivator.getDefault().getRepositorySystem();
IMavenExecutionContext context = maven.createExecutionContext();
List<ArtifactRepository> repositories = getAvailableArtifactRepositories(maven);
List<RemoteRepository> remoteRepositories = RepositoryUtils.toRepos(repositories);
VersionRangeRequest request = new VersionRangeRequest(artifact, remoteRepositories, null);
VersionRangeResult result = context.execute(new ICallable<VersionRangeResult>() {

@Override
public VersionRangeResult call(IMavenExecutionContext context, IProgressMonitor monitor)
throws CoreException {
RepositorySystemSession session = context.getRepositorySession();
try {
return repoSystem.resolveVersionRange(session, request);
} catch (VersionRangeResolutionException e) {
throw new CoreException(Status.error("Resolving latest version failed", e));
}
}
}, monitor);
Version highestVersion = result.getHighestVersion();
if (highestVersion == null || highestVersion.toString().equals(dependency.getVersion())) {
latest.add(dependency.copy());
} else {
latest.add(new MavenTargetDependency(dependency.getGroupId(), dependency.getArtifactId(),
highestVersion.toString(), dependency.getType(), dependency.getClassifier()));
MavenTargetDependency result = update(dependency, monitor);

latest.add(result);

if (!dependency.matches(result)) {
updated++;
}
}
Expand All @@ -436,6 +414,35 @@ public VersionRangeResult call(IMavenExecutionContext context, IProgressMonitor

}

public MavenTargetDependency update(MavenTargetDependency source, IProgressMonitor monitor) throws CoreException {
Artifact artifact = new DefaultArtifact(source.getGroupId() + ":" + source.getArtifactId() + ":(0,]");
IMaven maven = MavenPlugin.getMaven();
RepositorySystem repoSystem = MavenPluginActivator.getDefault().getRepositorySystem();
IMavenExecutionContext context = maven.createExecutionContext();
List<ArtifactRepository> repositories = getAvailableArtifactRepositories(maven);
List<RemoteRepository> remoteRepositories = RepositoryUtils.toRepos(repositories);
VersionRangeRequest request = new VersionRangeRequest(artifact, remoteRepositories, null);
VersionRangeResult result = context.execute(new ICallable<VersionRangeResult>() {
@Override
public VersionRangeResult call(IMavenExecutionContext context, IProgressMonitor monitor)
throws CoreException {
RepositorySystemSession session = context.getRepositorySession();
try {
return repoSystem.resolveVersionRange(session, request);
} catch (VersionRangeResolutionException e) {
throw new CoreException(Status.error("Resolving latest version failed", e));
}
}
}, monitor);
Version highestVersion = result.getHighestVersion();
if (highestVersion == null || highestVersion.toString().equals(source.getVersion())) {
return source.copy();
} else {
return new MavenTargetDependency(source.getGroupId(), source.getArtifactId(), highestVersion.toString(),
source.getType(), source.getClassifier());
}
}

private List<ArtifactRepository> getAvailableArtifactRepositories(IMaven maven) throws CoreException {
List<ArtifactRepository> repositories = new ArrayList<>(maven.getArtifactRepositories());
for (MavenTargetRepository repo : extraRepositories) {
Expand Down
1 change: 1 addition & 0 deletions org.eclipse.m2e.pde.ui/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Bundle-RequiredExecutionEnvironment: JavaSE-17
Require-Bundle: org.eclipse.core.runtime;bundle-version="3.19.0",
org.eclipse.jface,
org.eclipse.pde.ui;bundle-version="3.12.0",
org.eclipse.m2e.core.ui;bundle-version="2.0.3",
org.eclipse.m2e.maven.runtime;bundle-version="[3.8.6,4.0.0)",
org.eclipse.m2e.pde.target;bundle-version="[2.0.0,3.0.0)",
org.eclipse.core.databinding;bundle-version="1.10.100",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,20 @@
package org.eclipse.m2e.pde.ui.target.editor;

import java.io.ByteArrayInputStream;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.eclipse.core.runtime.Platform;
import org.eclipse.m2e.pde.target.MavenTargetDependency;
import org.eclipse.m2e.pde.target.MavenTargetLocation;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.widgets.Display;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
Expand Down Expand Up @@ -89,4 +94,49 @@ public List<MavenTargetDependency> getDependencies() {
return dependencies;
}

/**
* Attempts to retrieve all Maven dependencies from the clipboard. The
* dependencies are in the normal Maven format. Example:
*
* <pre>
* <dependency>
* <groupId>org.eclipse.jdt</groupId>
* <artifactId>org.eclipse.jdt.annotation</artifactId>
* <version>2.2.700</version>
* </dependency>
* </pre>
*
* The clipboard may contain one or more of those entries. On an ill-formed
* content, an exception is logged and an empty list returned.
*
* @param display The display on which to allocate the clipboard
* @return All dependencies which are stored in the clipboard. May be empty.
*/
public static List<MavenTargetDependency> getClipboardDependencies(Display display) {
String text = getClipboardContent(display);

ClipboardParser clipboardParser = new ClipboardParser(text);

try {
return clipboardParser.getDependencies();
} finally {
Exception clipboardError = clipboardParser.getError();

if (clipboardError != null) {
Platform.getLog(MavenTargetLocationWizard.class)
.warn(MessageFormat.format(Messages.ClipboardParser_1, clipboardError.getMessage()));
}
}
}

private static String getClipboardContent(Display display) {
Clipboard clipboard = new Clipboard(display);

try {
clipboard = new Clipboard(display);
return (String) clipboard.getContents(TextTransfer.getInstance());
} finally {
clipboard.dispose();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,198 +12,41 @@
*******************************************************************************/
package org.eclipse.m2e.pde.ui.target.editor;

import java.text.MessageFormat;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import org.apache.maven.model.Dependency;
import org.eclipse.core.databinding.observable.sideeffect.ISideEffectFactory;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.databinding.swt.ISWTObservableValue;
import org.eclipse.jface.databinding.swt.WidgetSideEffects;
import org.eclipse.jface.databinding.swt.typed.WidgetProperties;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.m2e.pde.target.MavenTargetDependency;
import org.eclipse.m2e.pde.target.MavenTargetLocation;
import org.eclipse.m2e.pde.ui.target.editor.internal.DependencyTable;
import org.eclipse.m2e.pde.ui.target.editor.internal.TargetDependencyModel;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabFolder2Adapter;
import org.eclipse.swt.custom.CTabFolderEvent;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.BorderLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

public class MavenTargetDependencyEditor {
private final Composite composite;
private final TargetDependencyModel model;

private static final String EDITOR_KEY = "MavenTargetDependencyEditor.editor";
private static final String DEPENDENCY_KEY = "MavenTargetDependencyEditor.dependency";
private static final ImageDescriptor ADD_IMAGE_DESCRIPTOR = ImageDescriptor
.createFromURL(MavenTargetLocationWizard.class.getResource("/icons/add_obj.png"));
private CTabFolder tabFolder;
public MavenTargetDependencyEditor(Composite parent, MavenTargetLocation targetLocation,
MavenTargetDependency selectedRoot) {
composite = new Composite(parent, SWT.NONE);
composite.setLayout(new BorderLayout());

public MavenTargetDependencyEditor(Composite parent, Collection<MavenTargetDependency> initialItems) {
tabFolder = new CTabFolder(parent, SWT.FLAT);
CTabItem addItem = new CTabItem(tabFolder, SWT.NONE);
addItem.setToolTipText("Add a new item");
Image image = ADD_IMAGE_DESCRIPTOR.createImage();
tabFolder.addDisposeListener(e -> image.dispose());
addItem.setImage(image);
tabFolder.addCTabFolder2Listener(new CTabFolder2Adapter() {
model = new TargetDependencyModel(targetLocation, selectedRoot);

@Override
public void close(CTabFolderEvent event) {
event.doit = tabFolder.getItemCount() > 2;

}
});
if (initialItems.isEmpty()) {
addNewItems(parent.getDisplay());
} else {
for (MavenTargetDependency dependency : initialItems) {
add(dependency.copy());
}
}
tabFolder.addSelectionListener(new SelectionListener() {

@Override
public void widgetSelected(SelectionEvent e) {
if (e.item == addItem) {
addNewItems(e.display);
}
}

@Override
public void widgetDefaultSelected(SelectionEvent e) {

}
});

}

private void addNewItems(Display display) {
Clipboard clipboard = new Clipboard(display);
String text = (String) clipboard.getContents(TextTransfer.getInstance());
clipboard.dispose();
ClipboardParser clipboardParser = new ClipboardParser(text);
List<MavenTargetDependency> dependencies = clipboardParser.getDependencies();
if (dependencies.isEmpty()) {
add(new MavenTargetDependency("", "", "", "", ""));
} else {
for (MavenTargetDependency mavenTargetDependency : dependencies) {
add(mavenTargetDependency);
}
}
Exception clipboardError = clipboardParser.getError();
if (clipboardError != null) {
Platform.getLog(MavenTargetLocationWizard.class)
.warn(MessageFormat.format(Messages.ClipboardParser_1, clipboardError.getMessage()));
}
}

private void add(MavenTargetDependency dependency) {
CTabItem newItem = new CTabItem(tabFolder, SWT.CLOSE);
newItem.setData(EDITOR_KEY, new DependencyEditor(tabFolder, dependency, newItem));
newItem.setData(DEPENDENCY_KEY, dependency);
tabFolder.setSelection(newItem);
new DependencyTable(composite, model);
}

public Control getControl() {
return tabFolder;
return composite;
}

private static final class DependencyEditor {

DependencyEditor(Composite parent, MavenTargetDependency dependency, CTabItem item) {
Composite composite = new Composite(parent, SWT.NONE);
composite.setLayout(new GridLayout(2, false));
ISideEffectFactory factory = WidgetSideEffects.createFactory(composite);
new Label(composite, SWT.NONE).setText(Messages.MavenTargetDependencyEditor_1);
ISWTObservableValue<String> groupId = WidgetProperties.text(SWT.Modify)
.observe(fill(new Text(composite, SWT.BORDER)));
new Label(composite, SWT.NONE).setText(Messages.MavenTargetDependencyEditor_2);
ISWTObservableValue<String> artifactId = WidgetProperties.text(SWT.Modify)
.observe(fill(new Text(composite, SWT.BORDER)));
new Label(composite, SWT.NONE).setText(Messages.MavenTargetDependencyEditor_3);
ISWTObservableValue<String> version = WidgetProperties.text(SWT.Modify)
.observe(fill(new Text(composite, SWT.BORDER)));
new Label(composite, SWT.NONE).setText(Messages.MavenTargetDependencyEditor_4);
ISWTObservableValue<String> classifier = WidgetProperties.text(SWT.Modify)
.observe(fill(new Text(composite, SWT.BORDER)));
new Label(composite, SWT.NONE).setText(Messages.MavenTargetDependencyEditor_5);
CCombo combo = combo(new CCombo(composite, SWT.BORDER));
combo.add("jar"); //$NON-NLS-1$
combo.add("bundle"); //$NON-NLS-1$
combo.add("pom"); //$NON-NLS-1$
ISWTObservableValue<String> type = WidgetProperties.ccomboSelection().observe(combo);
groupId.setValue(dependency.getGroupId());
artifactId.setValue(dependency.getArtifactId());
version.setValue(dependency.getVersion());
classifier.setValue(dependency.getClassifier());
type.setValue(dependency.getType());
factory.create(() -> {
dependency.setArtifactId(artifactId.getValue());
dependency.setGroupId(groupId.getValue());
dependency.setVersion(version.getValue());
dependency.setClassifier(classifier.getValue());
dependency.setType(type.getValue());
String key = dependency.getKey();
if (key.equals("::jar:")) {
item.setText(Messages.MavenTargetDependencyEditor_6);
} else {
item.setText(key);
}
});
item.setControl(composite);
parent.getDisplay().asyncExec(() -> {
((Control) groupId.getWidget()).forceFocus();
});
}

}

private static Text fill(Text text) {
GridData data = new GridData(GridData.FILL_HORIZONTAL);
data.grabExcessHorizontalSpace = true;
text.setLayoutData(data);
return text;
}

private static CCombo combo(CCombo combo) {
GridData data = new GridData();
data.widthHint = 100;
combo.setLayoutData(data);
return combo;
public boolean hasErrors() {
return model.hasErrors();
}

public Collection<MavenTargetDependency> getRoots() {
return Arrays.stream(tabFolder.getItems()).map(item -> item.getData(DEPENDENCY_KEY)).filter(Objects::nonNull)
.map(MavenTargetDependency.class::cast).collect(Collectors.toList());
}

public void setSelected(MavenTargetDependency selected) {
if (selected == null) {
tabFolder.setSelection(1);
return;
}
for (CTabItem item : tabFolder.getItems()) {
Object data = item.getData(DEPENDENCY_KEY);
if (data != null && selected.matches((Dependency) data)) {
tabFolder.setSelection(item);
return;
}
}
return new ArrayList<>(model.getTargetDependencies());
}
}
Loading

0 comments on commit 19a4aba

Please sign in to comment.