org.kaaproject.avro.ui.gwt.client.widget.ArrayFieldWidget.java Source code

Java tutorial

Introduction

Here is the source code for org.kaaproject.avro.ui.gwt.client.widget.ArrayFieldWidget.java

Source

/*
 * Copyright 2014-2015 CyberVision, Inc.
 *
 * Licensed under the Apache License, Version 2.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 org.kaaproject.avro.ui.gwt.client.widget;

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

import org.kaaproject.avro.ui.gwt.client.AvroUiResources.AvroUiStyle;
import org.kaaproject.avro.ui.gwt.client.util.Utils;
import org.kaaproject.avro.ui.gwt.client.widget.grid.AbstractGrid;
import org.kaaproject.avro.ui.gwt.client.widget.grid.event.RowActionEvent;
import org.kaaproject.avro.ui.gwt.client.widget.grid.event.RowActionEventHandler;
import org.kaaproject.avro.ui.gwt.client.widget.nav.NavigationActionListener;
import org.kaaproject.avro.ui.gwt.client.widget.nav.NavigationContainer;
import org.kaaproject.avro.ui.shared.ArrayField;
import org.kaaproject.avro.ui.shared.BooleanField;
import org.kaaproject.avro.ui.shared.FieldType;
import org.kaaproject.avro.ui.shared.FormField;
import org.kaaproject.avro.ui.shared.FormField.FieldAccess;
import org.kaaproject.avro.ui.shared.Fqn;
import org.kaaproject.avro.ui.shared.FqnKey;
import org.kaaproject.avro.ui.shared.RecordField;
import org.kaaproject.avro.ui.shared.UnionField;

import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.cellview.client.DataGrid;
import com.google.gwt.user.cellview.client.Header;
import com.google.gwt.user.cellview.client.SafeHtmlHeader;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.AsyncDataProvider;
import com.google.gwt.view.client.HasData;

public class ArrayFieldWidget extends AbstractFieldWidget<ArrayField> {

    private ArrayGrid arrayGrid;
    private ScrollPanel tableScroll;
    private FieldWidgetPanel fieldWidgetPanel;

    private static final String PX = "px";

    public ArrayFieldWidget(AvroWidgetsConfig config, NavigationContainer container, boolean readOnly) {
        super(config, container, readOnly);
    }

    public ArrayFieldWidget(AvroWidgetsConfig config, AvroUiStyle style, NavigationContainer container,
            boolean readOnly) {
        super(config, style, container, readOnly);
    }

    @Override
    protected Widget constructForm() {

        fieldWidgetPanel = new FieldWidgetPanel(style, value, readOnly, true);
        if (value.isOverride()) {
            fieldWidgetPanel.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
                @Override
                public void onValueChange(ValueChangeEvent<Boolean> event) {
                    if (!readOnly && !value.isReadOnly()) {
                        fireChanged();
                    }
                    if (event.getValue()) {
                        onShown();
                    }
                }
            });
            fieldWidgetPanel.setLegendNotes(
                    Utils.constants.getString(value.getOverrideStrategy().name().toLowerCase() + "Strategy"));
        }
        fieldWidgetPanel.setWidth(config.getArrayPanelWidth());

        value.finalizeMetadata();

        if (isGridNeeded(value)) {
            fieldWidgetPanel.setContent(constructGrid());
        } else {
            fieldWidgetPanel.setContent(constructTable());
        }
        return fieldWidgetPanel;
    }

    @Override
    public void updateConfig(AvroWidgetsConfig config) {
        super.updateConfig(config);
        if (fieldWidgetPanel != null) {
            fieldWidgetPanel.setWidth(config.getArrayPanelWidth());
        }
        if (arrayGrid != null) {
            arrayGrid.setHeight(config.getGridHeight());
        }
        if (tableScroll != null) {
            tableScroll.setHeight(config.getTableHeight());
            tableScroll.setWidth(getScrollTablePreferredWidth(config.getArrayPanelWidthPx()));
            tableScroll.getElement().getStyle().setMargin(AbstractGrid.DEFAULT_GRID_MARGIN, Unit.PX);
        }
    }

    public static boolean isGridNeeded(ArrayField field) {
        FormField metadata = field.getElementMetadata();
        if (metadata.getFieldType().isComplex()) {
            if (metadata.getFieldType() == FieldType.RECORD) {
                RecordField recordMetadata = (RecordField) metadata;
                if (!recordMetadata.getKeyIndexedFields().isEmpty()) {
                    return true;
                } else {
                    return hasComplexFields(
                            recordMetadata.getFieldsWithAccess(FieldAccess.EDITABLE, FieldAccess.READ_ONLY));
                }
            } else {
                return true;
            }
        } else {
            return false;
        }
    }

    private static boolean hasComplexFields(List<FormField> metaFields) {
        for (FormField metaField : metaFields) {
            if (metaField.getFieldType().isComplex()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public void onShown() {
        super.onShown();
        if (arrayGrid != null) {
            arrayGrid.reload();
        }
    }

    private Widget constructGrid() {
        VerticalPanel verticalPanel = new VerticalPanel();
        verticalPanel.setWidth(FULL_WIDTH);

        arrayGrid = new ArrayGrid(value, !readOnly && !value.isReadOnly());
        arrayGrid.setHeight(config.getGridHeight());

        verticalPanel.add(arrayGrid);

        HorizontalPanel buttonsPanel = new HorizontalPanel();
        buttonsPanel.addStyleName(style.buttonsPanel());

        if (!readOnly && !value.isReadOnly()) {
            Button addRow = new Button(Utils.constants.add());
            addRow.addStyleName(style.buttonSmall());
            addRow.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    FormField newField = value.createRow();
                    navigationContainer.addNewField(newField, new NavigationActionListener() {
                        @Override
                        public void onChanged(FormField field) {
                        }

                        @Override
                        public void onAdded(FormField field) {
                            arrayGrid.getDataProvider().addRow(field);
                        }
                    });
                }
            });
            buttonsPanel.add(addRow);
        }

        arrayGrid.addRowActionHandler(new RowActionEventHandler<Integer>() {
            @Override
            public void onRowAction(RowActionEvent<Integer> event) {
                final int index = event.getClickedId();
                if (event.getAction() == RowActionEvent.CLICK) {
                    FormField field = arrayGrid.getDataProvider().getData().get(index);
                    navigationContainer.showField(field, null);
                } else if (event.getAction() == RowActionEvent.DELETE) {
                    arrayGrid.getDataProvider().removeRow(index);
                    fireChanged();
                }
            }
        });

        verticalPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
        verticalPanel.add(buttonsPanel);
        return verticalPanel;
    }

    private Widget constructTable() {
        VerticalPanel verticalPanel = new VerticalPanel();
        tableScroll = new ScrollPanel();
        final FlexTable table = new FlexTable();
        table.setWidth("95%");
        table.setCellPadding(0);
        table.setCellSpacing(0);
        table.addStyleName(style.arrayTable());

        List<FormField> records = value.getValue();
        final FormField elementMetadata = value.getElementMetadata();

        final boolean hasHeader;

        if (elementMetadata.getFieldType() == FieldType.RECORD) {
            RecordField recordElementData = (RecordField) elementMetadata;
            float totalWeight = 0f;
            List<FormField> metaFields = recordElementData.getFieldsWithAccess(FieldAccess.EDITABLE,
                    FieldAccess.READ_ONLY);
            for (int column = 0; column < metaFields.size(); column++) {
                FormField metaField = metaFields.get(column);
                totalWeight += metaField.getWeight();
            }

            for (int column = 0; column < metaFields.size(); column++) {
                FormField metaField = metaFields.get(column);
                float weight = metaField.getWeight();
                String width = String.valueOf(weight / totalWeight * 100f) + "%";
                table.getColumnFormatter().setWidth(column, width);
                table.setWidget(0, column, new Label(metaField.getDisplayName()));
            }

            if (!readOnly) {
                table.setWidget(0, table.getCellCount(0), new Label(Utils.constants.delete()));
            }
            hasHeader = true;
        } else {
            hasHeader = false;
        }

        final Map<FormField, List<HandlerRegistration>> rowHandlerRegistrationMap = new HashMap<>();

        for (int row = 0; row < records.size(); row++) {
            FormField record = records.get(row);
            List<HandlerRegistration> rowHandlerRegistrations = new ArrayList<>();
            setRow(table, record, row, rowHandlerRegistrations, rowHandlerRegistrationMap, hasHeader);
            registrations.addAll(rowHandlerRegistrations);
            rowHandlerRegistrationMap.put(record, rowHandlerRegistrations);
        }

        tableScroll.setWidth(getScrollTablePreferredWidth(config.getArrayPanelWidthPx()));
        tableScroll.getElement().getStyle().setMargin(AbstractGrid.DEFAULT_GRID_MARGIN, Unit.PX);
        tableScroll.setHeight(config.getTableHeight());
        tableScroll.add(table);

        verticalPanel.setWidth(FULL_WIDTH);
        verticalPanel.add(tableScroll);

        if (!readOnly && !value.isReadOnly()) {
            Button addRow = new Button(Utils.constants.add());
            addRow.addStyleName(style.buttonSmall());

            addRow.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    FormField newField = value.createRow();
                    value.addArrayData(newField);
                    List<HandlerRegistration> rowHandlerRegistrations = new ArrayList<>();
                    setRow(table, newField, value.getValue().size() - 1, rowHandlerRegistrations,
                            rowHandlerRegistrationMap, hasHeader);
                    rowHandlerRegistrationMap.put(newField, rowHandlerRegistrations);
                    fireChanged();
                }
            });

            HorizontalPanel buttonsPanel = new HorizontalPanel();
            buttonsPanel.addStyleName(style.buttonsPanel());
            buttonsPanel.add(addRow);

            verticalPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
            verticalPanel.add(buttonsPanel);
        }
        return verticalPanel;
    }

    private void setRow(final FlexTable table, FormField field, int row,
            List<HandlerRegistration> handlerRegistrations,
            final Map<FormField, List<HandlerRegistration>> rowHandlerRegistrationMap, final boolean hasHeader) {
        if (hasHeader) {
            row++;
        }
        if (field.getFieldType() == FieldType.RECORD) {
            RecordField record = (RecordField) field;
            List<FormField> recordFields = record.getFieldsWithAccess(FieldAccess.EDITABLE, FieldAccess.READ_ONLY);
            for (int column = 0; column < recordFields.size(); column++) {
                FormField cellField = recordFields.get(column);
                constructAndPlaceWidget(table, cellField, row, column, handlerRegistrations);
            }
        } else {
            constructAndPlaceWidget(table, field, row, 0, handlerRegistrations);
        }

        if (!readOnly) {
            final Button delButton = new Button("");
            Image img = new Image(Utils.resources.remove());
            img.getElement().getStyle().setVerticalAlign(Style.VerticalAlign.MIDDLE);
            delButton.getElement().appendChild(img.getElement());
            delButton.addStyleName(style.cellButton());
            delButton.addStyleName(style.cellButtonSmall());
            delButton.getElement().getStyle().setMarginLeft(3, Unit.PX);
            HandlerRegistration handlerRegistration = delButton.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(ClickEvent event) {
                    int tableRow = table.getCellForEvent(event).getRowIndex();
                    int rowIndex = hasHeader ? tableRow - 1 : tableRow;
                    FormField toDelete = value.getValue().get(rowIndex);
                    List<HandlerRegistration> registrations = rowHandlerRegistrationMap.remove(toDelete);
                    if (registrations != null) {
                        for (HandlerRegistration registration : registrations) {
                            registration.removeHandler();
                        }
                        registrations.clear();
                    }
                    table.removeRow(tableRow);
                    value.getValue().remove(rowIndex);
                    fireChanged();
                }
            });
            handlerRegistrations.add(handlerRegistration);
            table.setWidget(row, table.getCellCount(row), delButton);
        }
    }

    private String getScrollTablePreferredWidth(int configWidth) {
        return (configWidth - AbstractGrid.DEFAULT_GRID_MARGIN * 2) + PX;
    }

    private static class ArrayGrid extends AbstractGrid<FormField, Integer> {

        private static final int MAX_CELL_STRING_LENGTH = 200;

        protected static final int DELETE_COLUMN_WIDTH = 70;

        private List<FormField> metadata;
        private ArrayDataProvider dataProvider;

        public ArrayGrid(ArrayField arrayField, boolean enableActions) {
            super(Unit.PX, enableActions, true, false);
            this.dataProvider = new ArrayDataProvider(arrayField);
            FormField elementMetadata = arrayField.getElementMetadata();
            if (elementMetadata.getFieldType() == FieldType.RECORD) {
                RecordField recordElementMetadata = (RecordField) elementMetadata;
                this.metadata = recordElementMetadata.getKeyIndexedFields();
                if (this.metadata.isEmpty()) {
                    this.metadata = ((RecordField) elementMetadata).getFieldsWithAccess(FieldAccess.EDITABLE,
                            FieldAccess.READ_ONLY);
                }
            } else if (elementMetadata.getFieldType() == FieldType.UNION && !elementMetadata.isOptional()) {
                UnionField unionElementMetadata = (UnionField) elementMetadata;
                List<FormField> acceptableValues = unionElementMetadata.getAcceptableValues();
                boolean useFirstRecord = true;
                List<FormField> keyIndexedFields = null;
                for (FormField acceptableValue : acceptableValues) {
                    if (acceptableValue != null && acceptableValue instanceof RecordField) {
                        List<FormField> recordKeyIndexedFields = ((RecordField) acceptableValue)
                                .getKeyIndexedFields();
                        if (recordKeyIndexedFields != null && !recordKeyIndexedFields.isEmpty()) {
                            if (keyIndexedFields == null) {
                                keyIndexedFields = recordKeyIndexedFields;
                            } else {
                                if (keyIndexedFields.size() != recordKeyIndexedFields.size()) {
                                    useFirstRecord = false;
                                    break;
                                } else {
                                    for (int i = 0; i < recordKeyIndexedFields.size(); i++) {
                                        if (!keyIndexedFields.get(i).getFieldName()
                                                .equals(recordKeyIndexedFields.get(i).getFieldName())) {
                                            useFirstRecord = false;
                                            break;
                                        }
                                    }
                                }
                            }
                        } else {
                            useFirstRecord = false;
                            break;
                        }
                    } else {
                        useFirstRecord = false;
                        break;
                    }
                }
                if (useFirstRecord && keyIndexedFields != null) {
                    this.metadata = keyIndexedFields;
                }
            }
            if (this.metadata == null) {
                this.metadata = new ArrayList<>();
                this.metadata.add(elementMetadata);
            }
            init();
            showShadow(false);
            this.dataProvider.addDataDisplay(getDataGrid());
        }

        public void reload() {
            this.dataProvider.reload(getDataGrid());
        }

        public ArrayDataProvider getDataProvider() {
            return this.dataProvider;
        }

        @Override
        protected float constructColumnsImpl(DataGrid<FormField> table) {
            float prefWidth = 0;
            for (final FormField metaField : metadata) {
                float width = 200 * metaField.getWeight();
                if (metaField.getFieldType() == FieldType.BOOLEAN) {
                    prefWidth += constructBooleanColumn(table, metaField.getDisplayName(),
                            new BooleanValueProvider<FormField>() {
                                @Override
                                public Boolean getValue(FormField item) {
                                    FormField field = item;
                                    if (field.getFieldType() == FieldType.RECORD) {
                                        field = ((RecordField) item).getFieldByName(metaField.getFieldName());
                                    }
                                    return ((BooleanField) field).getValue();
                                }
                            }, width);
                } else if (!metaField.getFieldType().isComplex()) {
                    prefWidth += constructStringColumn(table, metaField.getDisplayName(),
                            new StringValueProvider<FormField>() {
                                @Override
                                public String getValue(FormField item) {
                                    FormField field = item;
                                    if (field.getFieldType() == FieldType.RECORD) {
                                        field = ((RecordField) item).getFieldByName(metaField.getFieldName());
                                    } else if (field.getFieldType() == FieldType.UNION) {
                                        FormField unionVal = ((UnionField) field).getValue();
                                        field = ((RecordField) unionVal).getFieldByName(metaField.getFieldName());
                                    }
                                    String value = extractStringValue(field);
                                    if (value != null && value.length() > MAX_CELL_STRING_LENGTH) {
                                        value = value.substring(0, MAX_CELL_STRING_LENGTH - 3) + "...";
                                    }
                                    return value;
                                }
                            }, width);
                } else {
                    prefWidth += constructStringColumn(table, metaField.getDisplayName(),
                            new StringValueProvider<FormField>() {
                                @Override
                                public String getValue(FormField item) {
                                    FormField field = item;
                                    if (field.getFieldType() == FieldType.RECORD) {
                                        field = ((RecordField) item).getFieldByName(metaField.getFieldName());
                                    }
                                    String value = "";
                                    if (metadata.size() == 1) {
                                        int index = getObjectId(item);
                                        value = "#" + index + " ";
                                    }
                                    if (field.getFieldType() == FieldType.UNION) {
                                        FormField unionVal = ((UnionField) field).getValue();
                                        if (unionVal == null) {
                                            value += "null";
                                        } else {
                                            if (unionVal.getFieldType() == FieldType.RECORD) {
                                                RecordField recordField = ((RecordField) unionVal);
                                                if (recordField.isTypeConsumer()) {
                                                    String fqnString = "Invalid FQN reference";
                                                    FqnKey fqnKey = recordField.getConsumedFqnKey();
                                                    if (fqnKey != null) {
                                                        Fqn fqn = unionVal.getContext().getDeclaredTypes()
                                                                .get(fqnKey);
                                                        if (fqn != null) {
                                                            fqnString = fqn.getFqnString();
                                                        }
                                                    }
                                                    value += fqnString;
                                                } else if (recordField.isTypeHolder()) {
                                                    String fqnString = "Invalid FQN";
                                                    Fqn fqn = recordField.getDeclaredFqn();
                                                    if (fqn != null) {
                                                        fqnString = fqn.getFqnString();
                                                    }
                                                    value += fqnString;
                                                } else {
                                                    value += unionVal.getDisplayName();
                                                }
                                            } else {
                                                value += unionVal.getDisplayName();
                                            }
                                        }
                                    } else {
                                        value += field.getFieldType().getDisplayName();
                                    }
                                    if (value.length() > MAX_CELL_STRING_LENGTH) {
                                        value = value.substring(0, MAX_CELL_STRING_LENGTH - 3) + "...";
                                    }
                                    return value;
                                }
                            }, width);
                }
            }
            return prefWidth;
        }

        @Override
        protected float constructActions(DataGrid<FormField> table, float prefWidth) {
            if (enableActions) {
                if (deleteColumn == null || table.getColumnIndex(deleteColumn) == -1) {
                    Header<SafeHtml> deleteHeader = new SafeHtmlHeader(
                            SafeHtmlUtils.fromSafeConstant(Utils.constants.delete()));

                    deleteColumn = constructDeleteColumn("");
                    table.addColumn(deleteColumn, deleteHeader);
                    table.setColumnWidth(deleteColumn, DELETE_COLUMN_WIDTH, Unit.PX);
                    return DELETE_COLUMN_WIDTH;
                } else {
                    return 0;
                }
            } else {
                return 0;
            }
        }

        protected Integer getObjectId(FormField value) {
            return dataProvider.getData().indexOf(value);
        }
    }

    private static class ArrayDataProvider extends AsyncDataProvider<FormField> {

        private ArrayField arrayField;

        private boolean loaded = false;

        public ArrayDataProvider(ArrayField arrayField) {
            this.arrayField = arrayField;
        }

        public void addRow(FormField row) {
            arrayField.addArrayData(row);
            updateRowCount(arrayField.getValue().size(), true);
            updateRowData(arrayField.getValue().size() - 1,
                    arrayField.getValue().subList(arrayField.getValue().size() - 1, arrayField.getValue().size()));
        }

        public void removeRow(int index) {
            arrayField.removeRow(index);
            updateRowCount(arrayField.getValue().size(), true);
            int updateIndex = index;
            updateIndex = Math.min(updateIndex, arrayField.getValue().size() - 1);
            updateIndex = Math.max(0, updateIndex);
            updateRowData(updateIndex, arrayField.getValue().subList(updateIndex, arrayField.getValue().size()));
        }

        public List<FormField> getData() {
            return arrayField.getValue();
        }

        public void reload(HasData<FormField> display) {
            this.loaded = false;
            loadData(display);
        }

        @Override
        protected void onRangeChanged(final HasData<FormField> display) {
            if (!loaded) {
                loadData(display);
            } else {
                updateData(display);
            }
        }

        private void loadData(HasData<FormField> display) {
            updateRowCount(arrayField.getValue().size(), true);
            updateData(display);
            loaded = true;
        }

        private void updateData(HasData<FormField> display) {
            int start = display.getVisibleRange().getStart();
            int end = start + display.getVisibleRange().getLength();
            end = end >= arrayField.getValue().size() ? arrayField.getValue().size() : end;
            List<FormField> sub = arrayField.getValue().subList(start, end);
            updateRowData(start, sub);
        }

    }

}