All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jdplus.toolkit.desktop.plugin.components.parts.HasTsCollectionSupport Maven / Gradle / Ivy

/*
 * Copyright 2013 National Bank of Belgium
 *
 * Licensed under the EUPL, Version 1.1 or - as soon they will be approved
 * by the European Commission - subsequent versions of the EUPL (the "Licence");
 * You may not use this work except in compliance with the Licence.
 * You may obtain a copy of the Licence at:
 *
 * http://ec.europa.eu/idabc/eupl
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the Licence is distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the Licence for the specific language governing permissions and
 * limitations under the Licence.
 */
package jdplus.toolkit.desktop.plugin.components.parts;

import jdplus.toolkit.desktop.plugin.actions.Actions;
import jdplus.toolkit.desktop.plugin.beans.PropertyChangeBroadcaster;
import jdplus.toolkit.desktop.plugin.components.ComponentCommand;
import jdplus.toolkit.desktop.plugin.components.TsSelectionBridge;
import jdplus.toolkit.desktop.plugin.datatransfer.DataTransferManager;
import jdplus.toolkit.desktop.plugin.datatransfer.DataTransfers;
import jdplus.toolkit.desktop.plugin.datatransfer.LocalObjectDataTransfer;
import jdplus.toolkit.desktop.plugin.util.Collections2;
import jdplus.toolkit.desktop.plugin.util.KeyStrokes;
import jdplus.toolkit.base.api.timeseries.Ts;
import jdplus.toolkit.base.api.timeseries.TsCollection;
import jdplus.toolkit.base.api.timeseries.TsInformationType;
import jdplus.toolkit.base.api.timeseries.TsMoniker;
import jdplus.toolkit.base.tsp.DataSourceProvider;
import ec.util.list.swing.JLists;
import ec.util.various.swing.FontAwesome;
import ec.util.various.swing.JCommand;
import jdplus.toolkit.base.tsp.DataSet;
import jdplus.toolkit.desktop.plugin.*;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.util.Exceptions;
import org.openide.util.ImageUtilities;
import org.openide.util.Lookup;

import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.*;
import java.awt.event.ActionEvent;
import java.beans.BeanInfo;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.*;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author Philippe Charles
 */
@lombok.experimental.UtilityClass
public class HasTsCollectionSupport {

    @NonNull
    public static HasTsCollection of(@NonNull PropertyChangeBroadcaster broadcaster, TsInformationType info) {
        TsInformationType type = info.encompass(TsInformationType.Data) ? info : TsInformationType.Data;
        return new HasTsCollectionImpl(broadcaster, info, type).watch(TsManager.get());
    }

    @NonNull
    public static HasTsCollection of(@NonNull PropertyChangeBroadcaster broadcaster, TsInformationType broadcastinfo, TsInformationType loadinfo) {
        TsInformationType type = broadcastinfo.encompass(loadinfo) ? broadcastinfo : loadinfo;
        return new HasTsCollectionImpl(broadcaster, broadcastinfo, type).watch(TsManager.get());
    }

    @NonNull
    public static TransferHandler newTransferHandler(@NonNull HasTsCollection component) {
        return new HasTsCollectionTransferHandler(component, DataTransferManager.get());
    }

    @NonNull
    public static DropTargetListener newDropTargetListener(@NonNull HasTsCollection component, @NonNull DropTarget dropTarget) {
        return new HasTsCollectionDropTargetListener(component, DataTransferManager.get()).watch(dropTarget);
    }

    public static void registerActions(HasTsCollection component, ActionMap am) {
        am.put(HasTsCollection.FREEZE_ACTION, FreezeCommand.INSTANCE.toAction(component));
        am.put(HasTsCollection.COPY_ACTION, CopyCommand.INSTANCE.toAction(component));
        am.put(HasTsCollection.COPY_ALL_ACTION, CopyAllCommand.INSTANCE.toAction(component));
        am.put(HasTsCollection.DELETE_ACTION, DeleteCommand.INSTANCE.toAction(component));
        am.put(HasTsCollection.CLEAR_ACTION, ClearCommand.INSTANCE.toAction(component));
        am.put(HasTsCollection.PASTE_ACTION, PasteCommand.INSTANCE.toAction(component));
        am.put(HasTsCollection.OPEN_ACTION, OpenCommand.INSTANCE.toAction(component));
        am.put(HasTsCollection.SELECT_ALL_ACTION, SelectAllCommand.INSTANCE.toAction(component));
        am.put(HasTsCollection.RENAME_ACTION, RenameCommand.INSTANCE.toAction(component));
//        am.put(SPLIT_ACTION, HasChartCommands.splitIntoYearlyComponents().toAction(view));
//        if (this instanceof HasColorScheme) {
//            am.put(HasColorScheme.DEFAULT_COLOR_SCHEME_ACTION, HasColorScheme.commandOf(null).toAction((HasColorScheme) this));
//        }
    }

    public static void registerInputs(InputMap im) {
        KeyStrokes.putAll(im, KeyStrokes.COPY, HasTsCollection.COPY_ACTION);
        KeyStrokes.putAll(im, KeyStrokes.PASTE, HasTsCollection.PASTE_ACTION);
        KeyStrokes.putAll(im, KeyStrokes.DELETE, HasTsCollection.DELETE_ACTION);
        KeyStrokes.putAll(im, KeyStrokes.SELECT_ALL, HasTsCollection.SELECT_ALL_ACTION);
        KeyStrokes.putAll(im, KeyStrokes.OPEN, HasTsCollection.OPEN_ACTION);
        KeyStrokes.putAll(im, KeyStrokes.CLEAR, HasTsCollection.CLEAR_ACTION);
    }

    public static  JMenu newDefaultMenu(C component) {
        JMenu result = new JMenu();

        result.add(newOpenMenu(component));
        result.add(newOpenWithMenu(component));

        JMenu menu = newSaveMenu(component);
        if (menu.getSubElements().length > 0) {
            result.add(menu);
        }

        result.add(newRenameMenu(component));
        result.add(newFreezeMenu(component));
        result.add(newCopyMenu(component));
        result.add(newPasteMenu(component));
        result.add(newDeleteMenu(component));
        result.addSeparator();
        result.add(newSelectAllMenu(component));
        result.add(newClearMenu(component));

        return result;
    }

    public static  JMenuItem newRenameMenu(C component) {
        JMenuItem result = new JMenuItem(component.getActionMap().get(HasTsCollection.RENAME_ACTION));
        result.setText("Rename");
        result.setIcon(DemetraIcons.getPopupMenuIcon(FontAwesome.FA_PENCIL_SQUARE_O));
        Actions.hideWhenDisabled(result);
        return result;
    }

    public static  JMenuItem newOpenMenu(C component) {
        JMenuItem result = new JMenuItem(component.getActionMap().get(HasTsCollection.OPEN_ACTION));
        result.setText("Open");
        result.setIcon(DemetraIcons.getPopupMenuIcon(FontAwesome.FA_FOLDER_OPEN_O));
        Actions.hideWhenDisabled(result);
        result.setAccelerator(KeyStrokes.OPEN.get(0));
        result.setFont(result.getFont().deriveFont(Font.BOLD));
        return result;
    }

    public static  JMenu newOpenWithMenu(C component) {
        JMenu result = new JMenu(new MainOpenWithCommand().toAction(component));
        result.setText("Open with");
        result.setIcon(DemetraIcons.getPopupMenuIcon(FontAwesome.FA_BAR_CHART_O));
        Actions.hideWhenDisabled(result);

        for (NamedService o : TsActionManager.get().getOpenActions()) {
            JMenuItem item = new JMenuItem(((JCommand) new OpenWithCommand(o.getName())).toAction(component));
            item.setName(o.getName());
            item.setText(o.getDisplayName());
            if (DemetraUI.get().isPopupMenuIconsVisible()) {
                Image image = o.getIcon(BeanInfo.ICON_COLOR_16x16, false);
                if (image != null) {
                    item.setIcon(ImageUtilities.image2Icon(image));
                }
            }
            result.add(item);
        }
        return result;
    }

    public static  JMenu newSaveMenu(C component) {
        JMenu result = new JMenu(new MainSaveCommand().toAction(component));
        result.setText("Save");
        Actions.hideWhenDisabled(result);
        for (NamedService o : TsActionManager.get().getSaveActions()) {
            JMenuItem item = new JMenuItem(((JCommand) new SaveCommand(o)).toAction(component));
            item.setName(o.getName());
            item.setText(o.getDisplayName());
            if (DemetraUI.get().isPopupMenuIconsVisible()) {
                Image image = o.getIcon(BeanInfo.ICON_COLOR_16x16, false);
                if (image != null) {
                    item.setIcon(ImageUtilities.image2Icon(image));
                }
            }
            result.add(item);
        }
        return result;
    }

    public static  JMenuItem newCopyMenu(C component) {
        JMenuItem result = new JMenuItem(component.getActionMap().get(HasTsCollection.COPY_ACTION));
        result.setText("Copy");
        result.setIcon(DemetraIcons.getPopupMenuIcon(FontAwesome.FA_FILES_O));
        result.setAccelerator(KeyStrokes.COPY.get(0));
        Actions.hideWhenDisabled(result);
        return result;
    }

    public static  JMenuItem newPasteMenu(C component) {
        JMenuItem result = new JMenuItem(component.getActionMap().get(HasTsCollection.PASTE_ACTION));
        result.setText("Paste");
        result.setIcon(DemetraIcons.getPopupMenuIcon(FontAwesome.FA_CLIPBOARD));
        result.setAccelerator(KeyStrokes.PASTE.get(0));
//        ExtAction.hideWhenDisabled(item);
        return result;
    }

    public static  JMenuItem newDeleteMenu(C component) {
        JMenuItem result = new JMenuItem(component.getActionMap().get(HasTsCollection.DELETE_ACTION));
        result.setText("Remove");
        result.setIcon(DemetraIcons.getPopupMenuIcon(FontAwesome.FA_TRASH_O));
        result.setAccelerator(KeyStrokes.DELETE.get(0));
        Actions.hideWhenDisabled(result);
        return result;
    }

    public static  JMenuItem newClearMenu(C component) {
        JMenuItem result = new JMenuItem(component.getActionMap().get(HasTsCollection.CLEAR_ACTION));
        result.setText("Clear");
        result.setIcon(DemetraIcons.getPopupMenuIcon(FontAwesome.FA_ERASER));
        result.setAccelerator(KeyStrokes.CLEAR.get(0));
        return result;
    }

    public static  JMenuItem newSelectAllMenu(C component) {
        JMenuItem result = new JMenuItem(component.getActionMap().get(HasTsCollection.SELECT_ALL_ACTION));
        result.setText("Select all");
        result.setIcon(DemetraIcons.getPopupMenuIcon(FontAwesome.FA_ASTERISK));
        result.setAccelerator(KeyStrokes.SELECT_ALL.get(0));
        return result;
    }

    public static  JMenuItem newFreezeMenu(C component) {
        JMenuItem result = new JMenuItem(component.getActionMap().get(HasTsCollection.FREEZE_ACTION));
        result.setText("Freeze");
        result.setIcon(DemetraIcons.getPopupMenuIcon(FontAwesome.FA_LOCK));
        Actions.hideWhenDisabled(result);
        return result;
    }

    public static  JMenuItem newSplitMenu(C component) {
        JMenuItem item = new JMenuItem(component.getActionMap().get(HasTsCollection.SPLIT_ACTION));
        item.setText("Split into yearly components");
        item.setIcon(DemetraIcons.getPopupMenuIcon(FontAwesome.FA_CHAIN_BROKEN));
        Actions.hideWhenDisabled(item);
        return item;
    }

    //
    private static final class CopyAllCommand extends ComponentCommand {

        public static final CopyAllCommand INSTANCE = new CopyAllCommand();

        public CopyAllCommand() {
            super(HasTsCollection.TS_COLLECTION_PROPERTY);
        }

        @Override
        public boolean isEnabled(HasTsCollection component) {
            return !component.getTsCollection().isEmpty();
        }

        @Override
        public void execute(HasTsCollection c) throws Exception {
            Transferable transferable = DataTransferManager.get().fromTsCollection(c.getTsCollection());
            Toolkit.getDefaultToolkit().getSystemClipboard().setContents(transferable, null);
        }
    }

    private static abstract class SingleSelectionCommand extends ComponentCommand {

        public SingleSelectionCommand() {
            super(TsSelectionBridge.TS_SELECTION_PROPERTY);
        }

        @Override
        public boolean isEnabled(HasTsCollection c) {
            return JLists.isSingleSelectionIndex(c.getTsSelectionModel());
        }

        protected Ts getSingleTs(HasTsCollection c) {
            return c.getTsCollection().get(c.getTsSelectionModel().getMinSelectionIndex());
        }
    }

    private static abstract class AnySelectionCommand extends ComponentCommand {

        public AnySelectionCommand() {
            super(TsSelectionBridge.TS_SELECTION_PROPERTY);
        }

        @Override
        public boolean isEnabled(HasTsCollection component) {
            return !component.getTsSelectionModel().isSelectionEmpty();
        }
    }

    private static final class RenameCommand extends SingleSelectionCommand {

        public static final RenameCommand INSTANCE = new RenameCommand();

        @Override
        public void execute(final HasTsCollection c) throws Exception {
            final Ts ts = getSingleTs(c);
            NotifyDescriptor.InputLine descriptor = new NotifyDescriptor.InputLine("New name:", "Rename time series");
            descriptor.setInputText(ts.getName());
            if (ts.getMoniker().isProvided()) {
                descriptor.setAdditionalOptions(new Object[]{new JButton(new AbstractAction("Restore") {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        Optional provider = TsManager.get().getProvider(DataSourceProvider.class, ts.getMoniker());
                        if (provider.isPresent()) {
                            DataSet dataSet = provider.orElseThrow().toDataSet(ts.getMoniker()).orElse(null);
                            if (dataSet != null) {
                                rename(c, ts, provider.orElseThrow().getDisplayName(dataSet));
                            }
                        }
                    }
                })});
            }
            if (DialogDisplayer.getDefault().notify(descriptor) == NotifyDescriptor.OK_OPTION) {
                rename(c, ts, descriptor.getInputText());
            }
        }

        private void rename(HasTsCollection c, Ts ts, String newName) {
            List tmp = c.getTsCollection().toList();
            tmp.set(tmp.indexOf(ts), ts.withName(newName));
            c.setTsCollection(TsCollection.of(tmp));
        }
    }

    private static final class OpenCommand extends SingleSelectionCommand {

        public static final OpenCommand INSTANCE = new OpenCommand();

        @Override
        public void execute(HasTsCollection c) throws Exception {
            if (c instanceof HasTsAction) {
                String actionName = ((HasTsAction) c).getTsAction();
                if (actionName == null) {
                    actionName = DemetraBehaviour.get().getTsActionName();
                }
                TsActionManager.get().openWith(getSingleTs(c), actionName);
            }
        }
    }

    private static final class MainOpenWithCommand extends ComponentCommand {

        public MainOpenWithCommand() {
            super(TsSelectionBridge.TS_SELECTION_PROPERTY);
        }

        @Override
        public void execute(HasTsCollection component) throws Exception {
            // do nothing
        }

        @Override
        public boolean isEnabled(HasTsCollection component) {
            return JLists.isSingleSelectionIndex(component.getTsSelectionModel());
        }
    }

    @lombok.AllArgsConstructor
    private static final class OpenWithCommand extends SingleSelectionCommand {

        private final String tsAction;

        @Override
        public void execute(HasTsCollection c) throws Exception {
            TsActionManager.get().openWith(getSingleTs(c), tsAction);
        }
    }

    @lombok.AllArgsConstructor
    private static final class SaveCommand extends AnySelectionCommand {

        private final NamedService tsSave;

        @Override
        public void execute(HasTsCollection c) throws Exception {
            TsCollection selection = JLists.getSelectionIndexStream(c.getTsSelectionModel())
                    .mapToObj(c.getTsCollection()::get)
                    .collect(TsCollection.toTsCollection());
            if (!selection.isEmpty()) {
                List data = Collections.singletonList(selection);
                TsActionManager.get().saveWith(data, tsSave.getName());
            }
        }
    }

    private static final class CopyCommand extends AnySelectionCommand {

        public static final CopyCommand INSTANCE = new CopyCommand();

        @Override
        public void execute(HasTsCollection c) throws Exception {
            TsCollection data = c.getTsSelectionStream().collect(TsCollection.toTsCollection());
            Transferable transferable = DataTransferManager.get().fromTsCollection(data);
            Toolkit.getDefaultToolkit().getSystemClipboard().setContents(transferable, null);
        }
    }

    private static final class PasteCommand extends JCommand {

        public static final PasteCommand INSTANCE = new PasteCommand();

        @Override
        public boolean isEnabled(HasTsCollection c) {
            return !c.getTsUpdateMode().isReadOnly()
                    && DataTransferManager.get().isValidClipboard();
        }

        @Override
        public void execute(HasTsCollection c) throws Exception {
            HasTsCollectionTransferHandler.importData(c, DataTransferManager.get(), DataTransfers::systemClipboardAsTransferable);
        }

        @Override
        public ActionAdapter toAction(HasTsCollection c) {
            final ActionAdapter result = super.toAction(c);
            if (c instanceof Component) {
                result.withWeakPropertyChangeListener((Component) c, HasTsCollection.TS_UPDATE_MODE_PROPERTY);
            }
            DataTransferManager source = DataTransferManager.get();
            PropertyChangeListener realListener = evt -> result.refreshActionState();
            result.putValue("TssTransferSupport", realListener);
            source.addWeakPropertyChangeListener(DataTransferManager.VALID_CLIPBOARD_PROPERTY, realListener);
            return result;
        }
    }

    private static final class DeleteCommand extends ComponentCommand {

        public static final DeleteCommand INSTANCE = new DeleteCommand();

        public DeleteCommand() {
            super(TsSelectionBridge.TS_SELECTION_PROPERTY, HasTsCollection.TS_UPDATE_MODE_PROPERTY);
        }

        @Override
        public boolean isEnabled(HasTsCollection component) {
            return !component.getTsUpdateMode().isReadOnly()
                    && !component.getTsSelectionModel().isSelectionEmpty();
        }

        @Override
        public void execute(HasTsCollection c) throws Exception {
            Set selection = c.getTsSelectionStream().collect(Collectors.toSet());
            TsCollection result = c.getTsCollection()
                    .stream()
                    .filter(ts -> !selection.contains(ts))
                    .collect(TsCollection.toTsCollection());
            c.setTsCollection(result);
        }
    }

    private static final class ClearCommand extends ComponentCommand {

        public static final ClearCommand INSTANCE = new ClearCommand();

        public ClearCommand() {
            super(HasTsCollection.TS_COLLECTION_PROPERTY, HasTsCollection.TS_UPDATE_MODE_PROPERTY);
        }

        @Override
        public boolean isEnabled(HasTsCollection component) {
            return !component.getTsUpdateMode().isReadOnly() && !component.getTsCollection().isEmpty();
        }

        @Override
        public void execute(HasTsCollection component) throws Exception {
            component.setTsCollection(TsCollection.EMPTY);
        }
    }

    private static final class SelectAllCommand extends ComponentCommand {

        public static final SelectAllCommand INSTANCE = new SelectAllCommand();

        public SelectAllCommand() {
            super(TsSelectionBridge.TS_SELECTION_PROPERTY, HasTsCollection.TS_COLLECTION_PROPERTY);
        }

        @Override
        public boolean isEnabled(HasTsCollection c) {
            return JLists.getSelectionIndexSize(c.getTsSelectionModel()) != c.getTsCollection().size();
        }

        @Override
        public void execute(HasTsCollection c) throws Exception {
            c.getTsSelectionModel().setSelectionInterval(0, c.getTsCollection().size());
        }
    }

    private static final class FreezeCommand extends ComponentCommand {

        private static final FreezeCommand INSTANCE = new FreezeCommand();

        public FreezeCommand() {
            super(TsSelectionBridge.TS_SELECTION_PROPERTY, HasTsCollection.TS_UPDATE_MODE_PROPERTY);
        }

        @Override
        public boolean isEnabled(HasTsCollection component) {
            return JLists.isSingleSelectionIndex(component.getTsSelectionModel())
                    && !component.getTsUpdateMode().isReadOnly();
        }

        @Override
        public void execute(HasTsCollection c) throws Exception {
            JLists.getSelectionIndexStream(c.getTsSelectionModel())
                    .findFirst()
                    .ifPresent(i -> {
                        TsCollection tmp = c.getTsCollection();
                        List list = tmp.toList();
                        Ts s = list.get(i);
                        if (!s.getMoniker().isUserDefined()) {
                            list.add(s.freeze());
                            c.setTsCollection(tmp.toBuilder().clearItems().items(list).build());
                        }
                    });
        }
    }

    private static final class MainSaveCommand extends ComponentCommand {

        public MainSaveCommand() {
            super(TsSelectionBridge.TS_SELECTION_PROPERTY);
        }

        @Override
        public void execute(HasTsCollection component) throws Exception {
            // do nothing
        }

        @Override
        public boolean isEnabled(HasTsCollection component) {
            return !component.getTsSelectionModel().isSelectionEmpty();
        }
    }
    //

    /**
     * @author Philippe Charles
     */
    @lombok.RequiredArgsConstructor
    private static final class HasTsCollectionTransferHandler extends TransferHandler {

        @lombok.NonNull
        private final HasTsCollection delegate;

        @lombok.NonNull
        private final DataTransferManager dataTransfer;

        @Override
        public int getSourceActions(JComponent c) {
            //            TsDragRenderer r = selection.length < 10 ? TsDragRenderer.asChart() : TsDragRenderer.asCount();
            //            Image image = r.getTsDragRendererImage(Arrays.asList(selection));
            //            setDragImage(image);
            return COPY;
        }

        @Override
        protected Transferable createTransferable(JComponent c) {
            TsCollection data = delegate.getTsSelectionStream().collect(TsCollection.toTsCollection());
            return dataTransfer.fromTsCollection(data);
        }

        @Override
        public boolean canImport(TransferSupport support) {
            if (canImport(delegate, dataTransfer, support::getTransferable)) {
                if (support.isDrop()) {
                    support.setDropAction(COPY);
                }
                return true;
            }
            return false;
        }

        @Override
        public boolean importData(TransferSupport support) {
            return importData(delegate, dataTransfer, support::getTransferable);
        }

        public static boolean canImport(@NonNull HasTsCollection view, @NonNull DataTransferManager tssSupport, @NonNull Supplier toData) {
            if (!view.getTsUpdateMode().isReadOnly()) {
                Transferable t = toData.get();
                return tssSupport.canImport(t) && TransferChange.of(t, view.getTsCollection()).mayChangeContent();
            }
            return false;
        }

        public static boolean importData(@NonNull HasTsCollection view, @NonNull DataTransferManager tssSupport, @NonNull Supplier toData) {
            if (!view.getTsUpdateMode().isReadOnly()) {
                // merge the collections
                List all = tssSupport.toTsCollectionStream(toData.get()).collect(Collectors.toList());
                switch (all.size()) {
                    case 0:
                        return false;
                    case 1:
                        importData(view, all.get(0));
                        return true;
                    default:
                        TsCollection.Builder builder = TsCollection.builder();
                        all.forEach(z -> builder.items(z.getItems()));
                        TsCollection coll = builder.build();
                        if (!coll.isEmpty()) {
                            importData(view, coll);
                        }
                        return true;
                }
//                return tssSupport.toTsCollectionStream(toData.get())
//                        .peek(col -> importData(view, col))
//                        .count() > 0;
            }
            return false;
        }

        private static void importData(HasTsCollection view, TsCollection data) {
//            if (view.isFreezeOnImport() && TsManager.isDynamic(data)) {
//                TsCollection latest = TsManager.get().makeTsCollection(data.getMoniker(), TsInformationType.All);
//                view.setTsCollection(update(view.getTsUpdateMode(), view.getTsCollection(), frozenCopyOf(latest)));
//            } else {
            if (TransferChange.isNotYetLoaded(data)) {
                // TODO: put load in a separate thread
                data = data.load(TsInformationType.Definition, TsManager.get());
            }
            if (!data.isEmpty()) {
                view.setTsCollection(update(view.getTsUpdateMode(), view.getTsCollection(), data));
//                    TsManager.get().loadAsync(data, TsInformationType.All, view::replaceTsCollection);
            }
//            }
        }

        private static TsCollection update(HasTsCollection.TsUpdateMode mode, TsCollection first, TsCollection second) {
            switch (mode) {
                case None:
                    return first;
                case Single:
                    return TsCollection.of(second.get(0));
                case Replace:
                    return second;
                case Append:
                    Set firstMonikers = first.stream().map(Ts::getMoniker).collect(Collectors.toSet());
                    Predicate filter = moniker -> !moniker.isProvided() || !firstMonikers.contains(moniker);
                    return Stream.concat(first.stream(), second.stream().filter(Collections2.compose(filter, Ts::getMoniker))).collect(TsCollection.toTsCollection());
            }
            return first;
        }

        private enum TransferChange {
            YES, NO, MAYBE;

            public boolean mayChangeContent() {
                return this != NO;
            }

            public static TransferChange of(Transferable source, TsCollection target) {
                LocalObjectDataTransfer handler = Lookup.getDefault().lookup(LocalObjectDataTransfer.class);
                return handler != null ? of(handler, source, target) : MAYBE;
            }

            private static TransferChange of(LocalObjectDataTransfer handler, Transferable source, TsCollection target) {
                return DataTransfers.getMultiTransferables(source)
                        .map(handler::peekTsCollection)
                        .map(col -> of(col, target))
                        .filter(TransferChange::mayChangeContent)
                        .findFirst()
                        .orElse(NO);
            }

            private static TransferChange of(@Nullable TsCollection source, @NonNull TsCollection target) {
                if (source == null) {
                    return MAYBE;
                }
                if (isNotYetLoaded(source)) {
                    return MAYBE;
                }
                if (!source.stream().allMatch(target.getItems()::contains)) {
                    return YES;
                }
                return NO;
            }

            public static boolean isNotYetLoaded(TsCollection o) {
                return o.getMoniker().isProvided() && o.isEmpty();
            }
        }
    }

    /**
     * @author Philippe Charles
     */
    @lombok.RequiredArgsConstructor
    private static final class HasTsCollectionDropTargetListener implements DropTargetListener {

        @lombok.NonNull
        private final HasTsCollection target;

        @lombok.NonNull
        private final DataTransferManager transferSupport;

        @Override
        public void dragEnter(DropTargetDragEvent dtde) {
            if (!target.getTsUpdateMode().isReadOnly()) {
                Transferable t = dtde.getTransferable();
                if (transferSupport.canImport(t)) {
                    TsCollection dropContent = transferSupport
                            .toTsCollectionStream(t)
                            .flatMap(TsCollection::stream)
                            .collect(TsCollection.toTsCollection());
                    target.setDropContent(dropContent);
                }
            }
        }

        @Override
        public void dragOver(DropTargetDragEvent dtde) {
        }

        @Override
        public void dropActionChanged(DropTargetDragEvent dtde) {
        }

        @Override
        public void dragExit(DropTargetEvent dte) {
            target.setDropContent(TsCollection.EMPTY);
        }

        @Override
        public void drop(DropTargetDropEvent dtde) {
            dragExit(dtde);
        }

        public HasTsCollectionDropTargetListener watch(DropTarget dropTarget) {
            try {
                dropTarget.addDropTargetListener(this);
            } catch (TooManyListenersException ex) {
                Exceptions.printStackTrace(ex);
            }
            return this;
        }
    }

    /**
     * @author Philippe Charles
     */
    @lombok.RequiredArgsConstructor
    private static final class HasTsCollectionImpl implements HasTsCollection, TsListener {

        @lombok.NonNull
        private final PropertyChangeBroadcaster broadcaster;
        @lombok.NonNull
        private final TsInformationType broadcastInfo, loadInfo;

        private static final TsCollection DEFAULT_TS_COLLECTION = TsCollection.EMPTY;
        private TsCollection tsCollection = DEFAULT_TS_COLLECTION;

        @Override
        public TsCollection getTsCollection() {
            return tsCollection;
        }

        @Override
        public void setTsCollection(TsCollection tsCollection) {
            TsCollection old = this.tsCollection;
            this.tsCollection = tsCollection != null ? tsCollection : DEFAULT_TS_COLLECTION;
            boolean toload = tsCollection != null && !checkInfo(tsCollection, loadInfo);
            boolean tobroadcast = tsCollection == null || broadcastInfo == TsInformationType.None || checkInfo(tsCollection, broadcastInfo);
            if (toload) {
                TsManager.get().loadAsync(tsCollection, loadInfo, this::setTsCollection);
            }
            if (tobroadcast) {
                broadcaster.firePropertyChange(TS_COLLECTION_PROPERTY, old, this.tsCollection);
            }
        }

        private static boolean checkInfo(TsCollection coll, TsInformationType info) {
            return coll.getItems().stream().allMatch(s -> s.getType().encompass(info));
        }

        private static final Supplier DEFAULT_TS_SELECTION_MODEL = DefaultListSelectionModel::new;
        private ListSelectionModel tsSelectionModel = DEFAULT_TS_SELECTION_MODEL.get();

        @Override
        public ListSelectionModel getTsSelectionModel() {
            return tsSelectionModel;
        }

        @Override
        public void setTsSelectionModel(ListSelectionModel selectionModel) {
            ListSelectionModel old = this.tsSelectionModel;
            this.tsSelectionModel = selectionModel != null ? selectionModel : DEFAULT_TS_SELECTION_MODEL.get();
            broadcaster.firePropertyChange(TS_SELECTION_MODEL_PROPERTY, old, this.tsSelectionModel);
        }

        private static final TsUpdateMode DEFAULT_TS_UPDATE_MODE = TsUpdateMode.Append;
        private TsUpdateMode updateMode = DEFAULT_TS_UPDATE_MODE;

        @Override
        public TsUpdateMode getTsUpdateMode() {
            return updateMode;
        }

        @Override
        public void setTsUpdateMode(TsUpdateMode updateMode) {
            TsUpdateMode old = this.updateMode;
            this.updateMode = updateMode != null ? updateMode : DEFAULT_TS_UPDATE_MODE;
            broadcaster.firePropertyChange(TS_UPDATE_MODE_PROPERTY, old, this.updateMode);
        }

        private static final boolean DEFAULT_FREEZE_ON_IMPORT = false;
        private boolean freezeOnImport = DEFAULT_FREEZE_ON_IMPORT;

        @Override
        public boolean isFreezeOnImport() {
            return freezeOnImport;
        }

        @Override
        public void setFreezeOnImport(boolean freezeOnImport) {
            boolean old = this.freezeOnImport;
            this.freezeOnImport = freezeOnImport;
            broadcaster.firePropertyChange(FREEZE_ON_IMPORT_PROPERTY, old, this.freezeOnImport);
        }

        private static final TsCollection DEFAULT_DROP_CONTENT = TsCollection.EMPTY;
        private TsCollection dropContent = DEFAULT_DROP_CONTENT;

        @Override
        public TsCollection getDropContent() {
            return dropContent;
        }

        @Override
        public void setDropContent(TsCollection dropContent) {
            TsCollection old = this.dropContent;
            this.dropContent = dropContent != null ? dropContent : DEFAULT_DROP_CONTENT;
            broadcaster.firePropertyChange(DROP_CONTENT_PROPERTY, old, this.dropContent);
        }

        public HasTsCollectionImpl watch(TsManager manager) {
            manager.addWeakListener(this);
            return this;
        }

        @Override
        public void tsUpdated(TsEvent event) {
            TsCollection currentData = getTsCollection();
            if (isEventRelatedToCollection(event, currentData)) {
                TsCollection newData = event.getSource().makeTsCollection(currentData.getMoniker(), currentData.getType());
                setTsCollection(newData);
            } else {
                boolean requireChange = false;
                TsCollection.Builder newData = currentData.toBuilder().clearItems();
                for (Ts ts : currentData) {
                    if (event.getRelated().test(ts.getMoniker())) {
                        requireChange = true;
                        newData.item(event.getSource().makeTs(ts.getMoniker(), TsInformationType.All).withName(ts.getName()));
                    } else {
                        newData.item(ts);
                    }
                }
                if (requireChange) {
                    setTsCollection(newData.build());
                }
            }
        }

        private static boolean isEventRelatedToCollection(TsEvent event, TsCollection col) {
            return event.getRelated().test(col.getMoniker());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy