view gwt-client/src/main/java/org/dive4elements/river/client/client/ui/CollectionView.java @ 9416:05405292a7ca

Navigationtheme panel now shows themes of dWt and WQ charts grayed out, if the current station is outside the valid range of the theme.
author gernotbelger
date Thu, 16 Aug 2018 16:28:03 +0200
parents abf14917be32
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