Skip to content

Commit

Permalink
Add copy path actions
Browse files Browse the repository at this point in the history
  • Loading branch information
charphi committed Jan 31, 2025
1 parent 718471c commit f3305e8
Show file tree
Hide file tree
Showing 8 changed files with 406 additions and 0 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased]

### Added

- Add copy path actions

### Changed

- Modernize use of NIO API
Expand Down
5 changes: 5 additions & 0 deletions demetra-dotstat-desktop/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@
<artifactId>java-desktop-util-favicon</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>com.j2html</groupId>
<artifactId>j2html</artifactId>
<version>1.6.0</version>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package be.nbb.demetra.sdmx.web.actions;

import be.nbb.demetra.sdmx.web.SdmxWebBean;
import be.nbb.demetra.sdmx.web.SdmxWebProvider;
import ec.nbdemetra.ui.tsproviders.CollectionNode;
import ec.nbdemetra.ui.tsproviders.SeriesNode;
import ec.tss.tsproviders.DataSet;
import ec.tss.tsproviders.DataSource;
import ec.ui.ExtAction;
import internal.jd3.AbilityNodeAction3;
import internal.jd3.TsManager3;
import internal.sdmx.CatalogRef;
import internal.sdmx.OnDemandMenuBuilder;
import internal.sdmx.SdmxCommand;
import internal.sdmx.SdmxURI;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.awt.ActionRegistration;
import org.openide.util.NbBundle.Messages;
import org.openide.util.actions.Presenter;
import sdmxdl.Connection;
import sdmxdl.FlowRef;
import sdmxdl.Key;
import sdmxdl.Structure;

import javax.swing.*;
import java.io.IOException;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@ActionID(category = "Edit", id = CopyPathSetAction.ID)
@ActionRegistration(displayName = "#CTL_CopyPathSetAction", lazy = false)
@Messages("CTL_CopyPathSetAction=Copy Path/Reference...")
@ActionReferences({
@ActionReference(path = CollectionNode.ACTION_PATH, position = 1422, id = @ActionID(category = "Edit", id = CopyPathSetAction.ID)),
@ActionReference(path = SeriesNode.ACTION_PATH, position = 1422, id = @ActionID(category = "Edit", id = CopyPathSetAction.ID))
})
public final class CopyPathSetAction extends AbilityNodeAction3<DataSet> implements Presenter.Popup {

static final String ID = "be.nbb.demetra.sdmx.web.actions.CopyPathSetAction";

public CopyPathSetAction() {
super(DataSet.class, true);
}

@Override
public JMenuItem getPopupPresenter() {
return ExtAction.hideWhenDisabled(new JMenuItem(this));
}

@Override
protected void performAction(Stream<DataSet> items) {
DataSet item = single(items).orElseThrow(NoSuchElementException::new);
SdmxWebProvider provider = providerOf(item.getDataSource()).orElseThrow(NoSuchElementException::new);
SdmxWebBean bean = provider.decodeBean(item.getDataSource());
FlowRef flowRef = FlowRef.parse(bean.getFlow());
Key key = getKey(provider, bean.getSource(), flowRef, item);
CatalogRef catalog = CatalogRef.NO_CATALOG;
new OnDemandMenuBuilder()
.copyToClipboard("SDMX-DL URI", SdmxURI.dataSetURI(bean.getSource(), flowRef, key, catalog))
.copyToClipboard("Source", bean.getSource())
.copyToClipboard("Flow", flowRef.toString())
.copyToClipboard("Key", key.toString())
.addSeparator()
.copyToClipboard("Fetch data command", SdmxCommand.fetchData(catalog, bean.getSource(), flowRef.toString(), key))
.copyToClipboard("Fetch meta command", SdmxCommand.fetchMeta(catalog, bean.getSource(), flowRef.toString(), key))
.copyToClipboard("Fetch keys command", SdmxCommand.fetchKeys(catalog, bean.getSource(), flowRef.toString(), key))
.showMenuAsPopup(null);
}

@Override
protected boolean enable(Stream<DataSet> items) {
Optional<DataSet> item = single(items);
return item.isPresent() && providerOf(item.get().getDataSource()).isPresent();
}

@Override
public String getName() {
return Bundle.CTL_CopyPathSetAction();
}

private static Optional<SdmxWebProvider> providerOf(DataSource dataSource) {
return TsManager3.get().getProvider(SdmxWebProvider.class, dataSource);
}

private static <T> Optional<T> single(Stream<T> items) {
List<T> list = items.collect(Collectors.toList());
return list.size() == 1 ? Optional.of(list.get(0)) : Optional.empty();
}

private static Key getKey(SdmxWebProvider provider, String source, FlowRef flowRef, DataSet dataSet) {
try (Connection connection = provider.getSdmxManager().getConnection(source, provider.getLanguages())) {
Structure structure = connection.getStructure(flowRef);
Key.Builder result = Key.builder(structure);
structure.getDimensions().forEach(dimension -> result.put(dimension.getId(), dataSet.get(dimension.getId())));
return result.build();
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package be.nbb.demetra.sdmx.web.actions;

import be.nbb.demetra.sdmx.web.SdmxWebBean;
import be.nbb.demetra.sdmx.web.SdmxWebProvider;
import ec.nbdemetra.ui.tsproviders.DataSourceNode;
import ec.tss.tsproviders.DataSource;
import ec.ui.ExtAction;
import internal.jd3.AbilityNodeAction3;
import internal.jd3.TsManager3;
import internal.sdmx.CatalogRef;
import internal.sdmx.OnDemandMenuBuilder;
import internal.sdmx.SdmxCommand;
import internal.sdmx.SdmxURI;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionRegistration;
import org.openide.util.NbBundle.Messages;
import org.openide.util.actions.Presenter;
import sdmxdl.FlowRef;
import sdmxdl.Key;

import javax.swing.*;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@ActionID(category = "Edit", id = CopyPathSourceAction.ID)
@ActionRegistration(displayName = "#CTL_CopyPathSourceAction", lazy = false)
@Messages("CTL_CopyPathSourceAction=Copy Path/Reference...")
@ActionReference(path = DataSourceNode.ACTION_PATH, position = 1412, id = @ActionID(category = "Edit", id = CopyPathSourceAction.ID))
public final class CopyPathSourceAction extends AbilityNodeAction3<DataSource> implements Presenter.Popup {

static final String ID = "be.nbb.demetra.sdmx.web.actions.CopyPathSourceAction";

public CopyPathSourceAction() {
super(DataSource.class, true);
}

@Override
public JMenuItem getPopupPresenter() {
return ExtAction.hideWhenDisabled(new JMenuItem(this));
}

@SuppressWarnings("resource")
@Override
protected void performAction(Stream<DataSource> items) {
DataSource item = single(items).orElseThrow(NoSuchElementException::new);
SdmxWebProvider provider = providerOf(item).orElseThrow(NoSuchElementException::new);
SdmxWebBean bean = provider.decodeBean(item);
FlowRef flowRef = FlowRef.parse(bean.getFlow());
CatalogRef catalog = CatalogRef.NO_CATALOG;
new OnDemandMenuBuilder()
.copyToClipboard("SDMX-DL URI", SdmxURI.dataSourceURI(bean.getSource(), flowRef, catalog))
.copyToClipboard("Source", bean.getSource())
.copyToClipboard("Flow", flowRef.toString())
.addSeparator()
.copyToClipboard("List dimensions command", SdmxCommand.listDimensions(catalog, bean.getSource(), flowRef))
.copyToClipboard("List attributes command", SdmxCommand.listAttributes(catalog, bean.getSource(), flowRef))
.copyToClipboard("Fetch all keys command", SdmxCommand.fetchKeys(catalog, bean.getSource(), bean.getFlow(), Key.ALL))
.showMenuAsPopup(null);
}

@Override
protected boolean enable(Stream<DataSource> items) {
Optional<DataSource> item = single(items);
return item.isPresent() && providerOf(item.get()).isPresent();
}

@Override
public String getName() {
return Bundle.CTL_CopyPathSourceAction();
}

private static Optional<SdmxWebProvider> providerOf(DataSource dataSource) {
return TsManager3.get().getProvider(SdmxWebProvider.class, dataSource);
}

private static <T> Optional<T> single(Stream<T> items) {
List<T> list = items.collect(Collectors.toList());
return list.size() == 1 ? Optional.of(list.get(0)) : Optional.empty();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package internal.sdmx;

import lombok.AccessLevel;
import lombok.NonNull;

// FIXME: temporary code; to be removed when the real implementation is available in next release of sdmx-dl
@lombok.Value
@lombok.AllArgsConstructor(access = AccessLevel.PRIVATE)
public class CatalogRef {

@NonNull
String id;

@Override
public String toString() {
return id;
}

public static @NonNull CatalogRef parse(@NonNull CharSequence input) throws IllegalArgumentException {
return new CatalogRef(input.toString());
}

public static final CatalogRef NO_CATALOG = CatalogRef.parse("");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package internal.sdmx;

import j2html.TagCreator;
import j2html.tags.DomContent;
import j2html.tags.UnescapedText;

import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.StringSelection;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Objects;

import static j2html.TagCreator.*;

public final class OnDemandMenuBuilder {

private final JMenu result = new JMenu();

public OnDemandMenuBuilder copyToClipboard(String textMenu, String clipboardContent) {
addCopyToClipboard(result, textMenu, clipboardContent);
return this;
}

public static void addCopyToClipboard(JMenu menu, String textMenu, String clipboardContent) {
JMenuItem item = menu.add(copyToClipboard(clipboardContent));
item.setText(html(text(textMenu), SEPARATOR, preview(clipboardContent)).render());
item.setToolTipText(html(clipboardContent).render());
}

public OnDemandMenuBuilder openFolder(String textMenu, File folder) {
addOpenFolder(result, textMenu, folder);
return this;
}

public static void addOpenFolder(JMenu menu, String textMenu, File folder) {
JMenuItem item = menu.add(openFolder(folder));
item.setText(html(text(textMenu), SEPARATOR, preview(folder.getPath())).render());
item.setToolTipText(html(folder.getPath()).render());
}

public OnDemandMenuBuilder addSeparator() {
result.addSeparator();
return this;
}

public JMenu build() {
return result;
}

public void showMenuAsPopup(Component invoker) {
showMenuAsPopup(invoker, result);
}

public static void showMenuAsPopup(Component invoker, JMenu menu) {
if (invoker == null)
invoker = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
Point mousePosition = MouseInfo.getPointerInfo().getLocation();
SwingUtilities.convertPointFromScreen(mousePosition, invoker);
menu.getPopupMenu().show(invoker, mousePosition.x, mousePosition.y);
}

private static DomContent preview(String text) {
return TagCreator.span(text.length() > CHAR_LIMIT ? text.substring(0, CHAR_LIMIT) + "…" : text)
.withStyle("color:#0A84FF");
}

private static final int CHAR_LIMIT = 30;
private static final UnescapedText SEPARATOR = rawHtml("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");

private static AbstractAction copyToClipboard(String text) {
return new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(text), null);
}
};
}

private static AbstractAction openFolder(File folder) {
return new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
try {
ec.util.desktop.DesktopManager.get().showInFolder(Objects.requireNonNull(folder));
} catch (IOException ex) {
throw new UncheckedIOException(ex);
}
}
};
}
}
Loading

0 comments on commit f3305e8

Please sign in to comment.