com.haulmont.cuba.web.toolkit.ui.renderers.componentrenderer.ComponentRenderer.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.cuba.web.toolkit.ui.renderers.componentrenderer.ComponentRenderer.java

Source

/*
 * Licensed under the Apache License,Version2.0(the"License");you may not
 * use this file except in compliance with the License.You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,software
 * distributed under the License is distributed on an"AS IS"BASIS,WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND,either express or implied.See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.haulmont.cuba.web.toolkit.ui.renderers.componentrenderer;

import com.vaadin.data.Item;
import com.vaadin.server.ClientConnector;
import com.vaadin.server.communication.data.DataGenerator;
import com.vaadin.ui.Component;
import com.vaadin.ui.Grid;
import com.vaadin.ui.UI;
import elemental.json.Json;
import elemental.json.JsonObject;
import elemental.json.JsonValue;

import java.util.HashMap;

/**
 * A renderer for vaadin trackedComponents.
 *
 * Every component column must use its own renderer, so tracking the trackedComponents by column works correctly.
 *
 * @author Jonas Hahn (jonas.hahn@datenhahn.de)
 */
public class ComponentRenderer extends Grid.AbstractRenderer<Component> implements DataGenerator {

    /** Tracks this renderers trackedComponents to be able to unregister them from the grid upon removal
     *  of the renderers column */
    private final HashMap<Object, Component> trackedComponents = new HashMap<>();
    private boolean isRemovalInProgress = false;

    public ComponentRenderer() {
        super(Component.class, null);
    }

    @Override
    public JsonValue encode(Component component) {

        // 1: add component to grid, so connector id can be encoded
        if (component != null) {
            addComponentToGrid(component);
            return Json.create(component.getConnectorId());
        } else {
            return Json.createNull();
        }
    }

    /**
     * When the renderer is detached from the grid (e.g. when the column is removed)
     * release all trackedComponents to make them eligible for garbage collection and remove
     * the DataGenerator extension for this renderer from the Grid.
     *
     * @param parent the parent connector
     */
    @Override
    public void setParent(ClientConnector parent) {

        // detect a column removal (the renderer is being detached)
        if (getParent() != null && parent == null) {

            if (!isRemovalInProgress) {

                removeAllRendererComponentsFromGrid();

                // it is important to also detach the renderers @link{DataGenerator}
                // when the renderer is detached. The @link{com.vaadin.ui.Grid.AbstractGridExtension#remove}
                // does that, but calls setParent(null) again, which would lead to endless recursion
                // so we set a flag that we currently are already in removal
                // and stop further calls to remove().
                //
                isRemovalInProgress = true;
                remove();
            } else {
                isRemovalInProgress = false;
            }
        }

        super.setParent(parent);

        // VERY IMPORTANT: registers the DataGenerator extension
        // with the grid. The reason the extend method is deprecated
        // with renderers is that normal gwt-based renderers should not have
        // a direct dependency to the grid. In case of the componentrenderer
        // it must have this dependency to function properly.
        if (parent != null) {
            extend(getParentGrid());
        }
    }

    private void trackComponent(Object itemId, Component component) {

        // before tracking a new component for a row, it is necessary to remove the
        // previously tracked one from the grid to make the component eligible for
        // garbage collection.
        //
        // IMPORTANT: Only remove the component from the grid if
        // the new component is not the same as the old one. Otherwise
        // cached components also get removed.

        Component previousComponent = trackedComponents.get(itemId);

        if (previousComponent != component) {
            if (previousComponent != null) {
                safeRemoveComponentFromGrid(previousComponent);
            }
            trackedComponents.put(itemId, component);
        }
    }

    @Override
    public void generateData(Object itemId, Item item, JsonObject jsonRow) {

        JsonObject jsonData = jsonRow.getObject("d");

        for (String key : jsonData.keys()) {

            if (getColumn(key).getRenderer() == this) {
                // 2: VERY IMPORTANT get the component from the connector tracker !!!
                //    if you use a GeneratedPropertyContainer and call get Value you will
                //    get a different component

                if (jsonData.get(key) != Json.createNull()) {
                    Component current = lookupComponent(jsonData, key);
                    trackComponent(itemId, current);
                }
            }
        }
    }

    private Component lookupComponent(JsonObject jsonData, String key) {
        return (Component) UI.getCurrent().getConnectorTracker().getConnector(jsonData.getString(key));
    }

    @Override
    public void destroyData(Object itemId) {
        if (trackedComponents.containsKey(itemId)) {
            safeRemoveComponentFromGrid(trackedComponents.get(itemId));
            trackedComponents.remove(itemId);
        }

    }

    private void safeRemoveComponentFromGrid(Component component) {
        if (component != null) {
            removeComponentFromGrid(component);
        }
    }

    private void removeAllRendererComponentsFromGrid() {

        for (Component component : trackedComponents.values()) {
            safeRemoveComponentFromGrid(component);
        }

        trackedComponents.clear();
    }

}