view gwt-client/src/main/java/org/dive4elements/river/client/client/ui/CollectionView.java @ 9263:abf14917be32

Moved stepping behaviour of NaviOutputChart into an exchangeable strategy. Allows for distinct values stepping of sinfo flood duration.
author gernotbelger
date Tue, 17 Jul 2018 19:48:18 +0200
parents 23945061daec
children c81cf7e2a770
line wrap: on
line source
/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
 * Software engineering by Intevation GmbH
 *
 * This file is Free Software under the GNU AGPL (>=v3)
 * and comes with ABSOLUTELY NO WARRANTY! Check out the
 * documentation coming with Dive4Elements River for details.
 */

package org.dive4elements.river.client.client.ui;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;

import org.dive4elements.river.client.client.Config;
import org.dive4elements.river.client.client.FLYS;
import org.dive4elements.river.client.client.FLYSConstants;
import org.dive4elements.river.client.client.event.CollectionChangeEvent;
import org.dive4elements.river.client.client.event.CollectionChangeHandler;
import org.dive4elements.river.client.client.event.HasCollectionChangeHandlers;
import org.dive4elements.river.client.client.event.HasOutputModesChangeHandlers;
import org.dive4elements.river.client.client.event.OutputModesChangeEvent;
import org.dive4elements.river.client.client.event.OutputModesChangeHandler;
import org.dive4elements.river.client.client.event.ParameterChangeEvent;
import org.dive4elements.river.client.client.event.ParameterChangeHandler;
import org.dive4elements.river.client.client.services.AddArtifactService;
import org.dive4elements.river.client.client.services.AddArtifactServiceAsync;
import org.dive4elements.river.client.client.services.CollectionAttributeService;
import org.dive4elements.river.client.client.services.CollectionAttributeServiceAsync;
import org.dive4elements.river.client.client.services.CreateCollectionService;
import org.dive4elements.river.client.client.services.CreateCollectionServiceAsync;
import org.dive4elements.river.client.client.services.DescribeCollectionService;
import org.dive4elements.river.client.client.services.DescribeCollectionServiceAsync;
import org.dive4elements.river.client.client.services.LoadArtifactService;
import org.dive4elements.river.client.client.services.LoadArtifactServiceAsync;
import org.dive4elements.river.client.shared.model.Artifact;
import org.dive4elements.river.client.shared.model.ArtifactDescription;
import org.dive4elements.river.client.shared.model.Collection;
import org.dive4elements.river.client.shared.model.ExportMode;
import org.dive4elements.river.client.shared.model.OutputMode;
import org.dive4elements.river.client.shared.model.Recommendation;
import org.dive4elements.river.client.shared.model.ReportMode;
import org.dive4elements.river.client.shared.model.User;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.Window;
import com.smartgwt.client.widgets.events.CloseClickEvent;
import com.smartgwt.client.widgets.events.CloseClickHandler;
import com.smartgwt.client.widgets.layout.Layout;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.tab.TabSet;
import com.smartgwt.client.widgets.tab.events.TabSelectedHandler;

/**
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public class CollectionView extends Window implements CollectionChangeHandler, HasCollectionChangeHandlers, OutputModesChangeHandler, HasOutputModesChangeHandlers, ParameterChangeHandler, CloseClickHandler {

    /** The message class that provides i18n strings. */
    private static final FLYSConstants messages = GWT.create(FLYSConstants.class);

    /** The ArtifactService used to communicate with the Artifact server. */
    private final CreateCollectionServiceAsync createCollectionService = GWT.create(CreateCollectionService.class);

    /** The AddArtifactService used to add an artifact to a collection. */
    private final AddArtifactServiceAsync addArtifactService = GWT.create(AddArtifactService.class);

    /** The DescribeCollectionService used to update the existing collection. */
    private final DescribeCollectionServiceAsync describeCollectionService = GWT.create(DescribeCollectionService.class);

    private final CollectionAttributeServiceAsync updater = GWT.create(CollectionAttributeService.class);

    /** The LoadArtifactService used to load recommendations */
    private final LoadArtifactServiceAsync loadArtifactService = GWT.create(LoadArtifactService.class);

    /** The FLYS instance used to call services. */
    private final FLYS flys;

    /** The ParameterList. */
    private ParameterList parameterList;

    /** The list of CollectionChangeHandlers. */
    private final List<CollectionChangeHandler> handlers;

    /** The list of OutputModesChangeHandlers. */
    private final List<OutputModesChangeHandler> outHandlers;

    /** The collection to be displayed. */
    private Collection collection;

    /** The artifact that handles the parameterization. */
    private Artifact artifact;

    private final TabSet tabs;

    /** The output tab. */
    private final Map<String, OutputTab> outputTabs;

    /** The layout. */
    private final Layout layout;

    /** Layout to show spinning wheel of joy. */
    private VLayout lockScreen;

    private final int artifactsQueue;
    private final Stack<Recommendation> newRecommendations;

    /** Values for fix analysis charts */
    private double currentKm;

    /**
     * This constructor creates a new CollectionView that is used to display the
     * <i>collection</i>.
     */
    public CollectionView(final FLYS flys) {
        this.flys = flys;
        this.tabs = new TabSet();
        this.outputTabs = new HashMap<String, OutputTab>();
        this.handlers = new ArrayList<CollectionChangeHandler>();
        this.outHandlers = new ArrayList<OutputModesChangeHandler>();
        this.layout = new VLayout();
        this.parameterList = new ParameterList(flys, this, CollectionView.messages.new_project());
        this.artifactsQueue = 0;
        this.newRecommendations = new Stack<Recommendation>();

        this.currentKm = -1d;

        addCollectionChangeHandler(this);
        addCollectionChangeHandler(this.parameterList);
        addCollectionChangeHandler(flys);
        addOutputModesChangeHandler(this);
        addOutputModesChangeHandler(this.parameterList);
        addCloseClickHandler(this);

        this.parameterList.addParameterChangeHandler(this);

        init();
    }

    /**
     * @param collection
     *            The collection to be displayed.
     */
    public CollectionView(final FLYS flys, final Collection collection, final Artifact artifact) {
        this.flys = flys;
        this.artifact = artifact;
        this.collection = collection;
        this.tabs = new TabSet();
        this.outputTabs = new HashMap<String, OutputTab>();
        this.handlers = new ArrayList<CollectionChangeHandler>();
        this.outHandlers = new ArrayList<OutputModesChangeHandler>();
        this.layout = new VLayout();

        this.currentKm = -1d;

        if (artifact != null) {
            this.parameterList = new ParameterList(flys, this,
                    // FIXME: literally every information about the artifact is transported from the server side
                    // but... the international name is resolved client-side.... Instead also transport the description of the artifact and
                    // use it!
                    // FIXME: the same holds for a very few other international strings (e.g. names of facets used in Tabs)
                    CollectionView.messages.getString(artifact.getName()), artifact);
        } else {
            this.parameterList = new ParameterList(flys, this, CollectionView.messages.new_project());
        }

        this.artifactsQueue = 0;
        this.newRecommendations = new Stack<Recommendation>();

        addCollectionChangeHandler(this);
        addCollectionChangeHandler(this.parameterList);
        addCollectionChangeHandler(flys);
        addOutputModesChangeHandler(this);
        addOutputModesChangeHandler(this.parameterList);
        addCloseClickHandler(this);

        this.parameterList.addParameterChangeHandler(this);

        init();

        setCollection(collection);

        if (artifact != null) {
            setArtifact(artifact);
        }
    }

    /**
     * This method handles the initial layout stuff.
     */
    private void init() {
        setWidth(1010);
        setHeight(700);

        setMaximized(true);

        this.layout.setWidth100();

        setCanDragReposition(true);
        setCanDragResize(true);
        setShowMaximizeButton(true);
        setKeepInParentRect(true);

        setTitle("");

        addItem(this.layout);

        this.layout.addMember(this.tabs);
        this.tabs.addTab(this.parameterList);
    }

    private FLYS getFlys() {
        return this.flys;
    }

    /**
     * This method registers a new CollectionChangeHandler.
     *
     * @param handler
     *            The new CollectionChangeHandler.
     */
    @Override
    public void addCollectionChangeHandler(final CollectionChangeHandler handler) {
        if (handler != null) {
            this.handlers.add(handler);
        }
    }

    /**
     * This method registers a new OutputModesChangeHandler.
     *
     * @param handler
     *            The new OutputModesChangeHandler.
     */
    @Override
    public void addOutputModesChangeHandler(final OutputModesChangeHandler handler) {
        if (handler != null) {
            this.outHandlers.add(handler);
        }
    }

    /**
     * This method calls the <code>onValueChange()</code> method of all
     * registered ValueChangeHanders.
     */
    private void fireCollectionChangeEvent(final Collection old, final Collection newCol) {
        for (final CollectionChangeHandler handler : this.handlers) {
            handler.onCollectionChange(new CollectionChangeEvent(old, newCol));
        }
    }

    private void fireOutputModesChangeEvent(final OutputMode[] outputs) {
        if (this.collection == null) {
            return;
        }

        for (final OutputModesChangeHandler handler : this.outHandlers) {
            handler.onOutputModesChange(new OutputModesChangeEvent(outputs));
        }
    }

    /** Disables input, grey out, show spinning wheel of joy. */
    public void lockUI() {
        this.lockScreen = ScreenLock.lockUI(this.layout, this.lockScreen);
    }

    /** Enable input, remove grey, remove spinning wheel of joy. */
    public void unlockUI() {
        ScreenLock.unlockUI(this.layout, this.lockScreen);
    }

    /**
     * Returns the artifact that is used for the parameterization.
     *
     * @return the artifact that is used for the parameterization.
     */
    public Artifact getArtifact() {
        return this.artifact;
    }

    public User getUser() {
        return getFlys().getCurrentUser();
    }

    /**
     * Set the current artifact that is the master of the parameterization.
     *
     * @param artifact
     *            The new artifact.
     */
    public void setArtifact(final Artifact artifact) {
        this.artifact = artifact;

        artifactChanged();

        if (artifact.isInBackground()) {
            final LoadingPanel p = new LoadingPanel(this, artifact);
            p.addStepBackHandler(this.parameterList);
        }
    }

    /**
     * Implements the onCollectionChange() method to do update the GUI after the
     * parameterization has changed.
     *
     * @param event
     *            The ParameterChangeEvent.
     */
    @Override
    public void onParameterChange(final ParameterChangeEvent event) {
        GWT.log("CollectionView.onParameterChange");
        setArtifact(event.getNewValue());
    }

    private void artifactChanged() {
        final Collection c = getCollection();

        if (c != null) {
            loadCollection(c);
        } else {
            updateView();
        }
    }

    /**
     * Loads all information of a collection.
     * If 'recommendations' present, load these.
     *
     * @param c
     *            the Collection
     */
    private void loadCollection(final Collection c) {
        final ArtifactDescription desc = getArtifact().getArtifactDescription();
        final Recommendation[] recom = desc.getRecommendations();
        final String locale = Config.getInstance().getLocale();

        this.describeCollectionService.describe(c.identifier(), locale, new AsyncCallback<Collection>() {
            @Override
            public void onFailure(final Throwable caught) {
                GWT.log("Could not DESCRIBE collection.");
                SC.warn(FLYS.getExceptionString(CollectionView.this.messages, caught));
            }

            @Override
            public void onSuccess(final Collection newCollection) {
                GWT.log("Successfully DESCRIBED collection.");
                boolean loaded = true;
                for (final Recommendation r : recom) {
                    if (!newCollection.loadedRecommendation(r)) {
                        loaded = false;
                    }
                }
                if (!loaded) {
                    loadRecommendedArtifacts(recom);
                } else {
                    setCollection(newCollection);
                }
            }
        });
    }

    /**
     * Returns the collection of displayed by this view.
     *
     * @return the collection of this view.
     */
    public Collection getCollection() {
        return this.collection;
    }

    private void setCollection(final Collection collection) {
        setCollection(collection, false);
    }

    /**
     * Set the current collection.
     *
     * @param collection
     *            The new collection.
     * @param suppress
     *            Whether to fire a collectionchangeevent.
     */
    public void setCollection(final Collection collection, final boolean suppress) {
        if (collection != null && this.collection == null) {
            this.flys.getWorkspace().addView(collection.identifier(), this);
        }

        final Collection tmp = this.collection;
        this.collection = collection;

        if (collection != null)
            setTitle(collection.getDisplayName());

        if (!suppress)
            fireCollectionChangeEvent(tmp, this.collection);
    }

    @Override
    public void onCollectionChange(final CollectionChangeEvent event) {
        if (this.artifactsQueue > 0) {
            GWT.log("Do not update UI because we are still loading Artifacts.");
            return;
        }

        final Collection newCol = event.getNewValue();

        final Map<String, OutputMode> outs = newCol.getOutputModes();

        final Set<String> keys = outs.keySet();
        final OutputMode[] prepared = new OutputMode[outs.size()];

        int idx = 0;
        for (final String outname : keys) {
            prepared[idx++] = outs.get(outname);
        }

        fireOutputModesChangeEvent(prepared);

        updateView();
    }

    @Override
    public void onOutputModesChange(final OutputModesChangeEvent event) {
        clearOutputTabs();
        final OutputMode[] outs = event.getOutputModes();

        if (outs == null) {
            return;
        }

        boolean hasCSV = false;

        for (final OutputMode out : outs) {
            addOutputTab(out.getName(), out);

            if (out instanceof ExportMode) {
                final ExportMode export = (ExportMode) out;

                if (export.getFacet("csv") != null) {
                    hasCSV = true;
                }
            }
        }

        if (!hasCSV) {
            this.parameterList.removeTable();
        }
    }

    /**
     * Adds a new tab for the OutputMode <i>out</i>.
     *
     * @param name
     *            The name and title of the output.
     */
    private void addOutputTab(final String name, final OutputMode out) {
        if (out instanceof ExportMode) {
            final ExportMode export = (ExportMode) out;

            if (export.getFacet("csv") != null && !this.parameterList.hasTable()) {
                final TableDataPanel p = new TableDataPanel();
                p.setUuid(this.collection.identifier());
                p.setName(out.getName());
                this.parameterList.setTable(p);
            }

            return;
        }

        if (out instanceof ReportMode) {
            // we don't want to display report modes at all
            return;
        }

        GWT.log("Add new output tab for '" + name + "'");

        final String title = CollectionView.messages.getString(name);
        final OutputTab tab = out.createOutputTab(title, getCollection(), this);

        if (tab != null)
            this.outputTabs.put(name, tab);
    }

    /**
     * Removes all output mode tabs from tab bar.
     */
    private void clearOutputTabs() {
        GWT.log("Clear OutputTabs.");

        final int num = this.tabs.getNumTabs();

        for (int i = num - 1; i >= 1; i--) {
            this.tabs.removeTab(i);
        }

        this.outputTabs.clear();
    }

    /**
     * Update the view (refresh the list of old and current data).
     */
    private void updateView() {
        GWT.log("CollectionView.updateView()");
        updateOutputTabs();
    }

    /**
     * This method is used to update the tabs to show specific output modes.
     */
    private void updateOutputTabs() {
        GWT.log("Update output tabs.");
        if (this.outputTabs != null) {
            final Set<String> keys = this.outputTabs.keySet();

            for (final String key : keys) {
                this.tabs.addTab(this.outputTabs.get(key));
            }
        }
    }

    @Override
    public void onCloseClick(final CloseClickEvent event) {
        if (this.collection != null) {
            if (this.artifact != null) {
                this.flys.closeProject(this.collection.identifier());
            } else {
                this.flys.getProjectList().deleteCollection(this.collection);
            }
        } else {
            hide();
            destroy();
        }
    }

    public void addArtifactToCollection(final Artifact artifact) {
        final String locale = Config.getInstance().getLocale();

        GWT.log("CollectionView.addArtifactToCollection " + this.collection);

        if (this.collection != null) {
            this.addArtifactService.add(this.collection, artifact, locale, new AsyncCallback<Collection>() {
                @Override
                public void onFailure(final Throwable caught) {
                    GWT.log("An error occured while adding artifact.");
                    SC.warn(FLYS.getExceptionString(CollectionView.this.messages, caught));
                }

                @Override
                public void onSuccess(final Collection newCollection) {
                    GWT.log("Successfully added artifacts.");
                    setCollection(newCollection);
                }
            });
        } else {
            // Create new collection and add artifact.
            final Artifact art = artifact;
            this.createCollectionService.create(locale, this.flys.getCurrentUser().identifier(), new AsyncCallback<Collection>() {
                @Override
                public void onFailure(final Throwable caught) {
                    GWT.log("Could not create the new collection.");
                    SC.warn(FLYS.getExceptionString(CollectionView.messages, caught));
                }

                @Override
                public void onSuccess(final Collection collection) {
                    GWT.log("Successfully created a new collection.");
                    CollectionView.this.addArtifactService.add(collection, art, locale, new AsyncCallback<Collection>() {
                        @Override
                        public void onFailure(final Throwable caught) {
                            GWT.log("An error occured while " + "adding artifact.");
                            SC.warn(FLYS.getExceptionString(CollectionView.this.messages, caught));
                        }

                        @Override
                        public void onSuccess(final Collection newCollection) {
                            GWT.log("Successfully added artifacts.");
                            setCollection(newCollection);
                        }
                    });
                }
            });
        }
    }

    private void addRecommendationsToCollection() {
        final String locale = Config.getInstance().getLocale();

        this.collection.addRecommendations(this.newRecommendations);

        this.updater.update(this.collection, locale, new AsyncCallback<Collection>() {
            @Override
            public void onFailure(final Throwable caught) {
                CollectionView.this.newRecommendations.removeAllElements();
                setCollection(CollectionView.this.collection);

                GWT.log("An error occured while saving recommendations.");
                SC.warn(FLYS.getExceptionString(CollectionView.this.messages, caught));
            }

            @Override
            public void onSuccess(final Collection newCol) {
                GWT.log("Successfully saved recommendations.");
                CollectionView.this.newRecommendations.removeAllElements();
                setCollection(newCol);
            }
        });
    }

    private void loadRecommendedArtifacts(final Recommendation[] recommendations) {
        final String locale = Config.getInstance().getLocale();

        final Artifact masterArtifact = getArtifact();

        if (recommendations == null) {
            GWT.log("WARNING: Currently no recommendations.");
            return;
        }

        for (final Recommendation recommendation : recommendations) {
            if (this.collection.loadedRecommendation(recommendation)) {
                continue;
            }
            this.newRecommendations.push(recommendation);

            // XXX: UGLY! If no reference artifact given use uuid of
            // current artifact as reference.
            if (recommendation.getMasterArtifact() == null) {
                recommendation.setMasterArtifact(masterArtifact.getUuid());
            }

        }

        this.loadArtifactService.loadMany(this.collection, recommendations, null, locale, new AsyncCallback<Artifact[]>() {
            @Override
            public void onFailure(final Throwable caught) {
                GWT.log("Error loading recommendations: " + caught.getMessage());
                SC.warn(FLYS.getExceptionString(CollectionView.messages, caught));
            }

            @Override
            public void onSuccess(final Artifact[] artifacts) {
                GWT.log("Loaded artifacts: " + artifacts.length);
                addRecommendationsToCollection();
            }
        });
    }

    public void registerTabHandler(final TabSelectedHandler tse) {
        this.tabs.addTabSelectedHandler(tse);
    }

    public void setCurrentKm(final double currentKm) {
        this.currentKm = currentKm;
    }

    public double getCurrentKm() {
        return this.currentKm;
    }
}

http://dive4elements.wald.intevation.org