Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show Maven artifacts via list instead of tabs for the dependency editor. #1157

Merged
merged 2 commits into from
Jan 19, 2023
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Show Maven artifacts via table instead of tabs in the dependency editor.
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.
ptziegler authored and HannesWell committed Jan 16, 2023
commit ac69c5ff0c801427d6eaf1cfa31570bf91265491
6 changes: 6 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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++;
}
}
@@ -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) {
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
@@ -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",
Original file line number Diff line number Diff line change
@@ -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;
@@ -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
@@ -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