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

io.devbench.uibuilder.data.collectionds.CollectionDataSource Maven / Gradle / Ivy

/*
 *
 * Copyright © 2018 Webvalto Ltd.
 *
 * 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 io.devbench.uibuilder.data.collectionds;

import io.devbench.uibuilder.core.utils.reflection.ClassMetadata;
import io.devbench.uibuilder.core.utils.reflection.PropertyMetadata;
import io.devbench.uibuilder.data.api.filter.FilterExpressionFactory;
import io.devbench.uibuilder.data.api.order.OrderExpressionFactory;
import io.devbench.uibuilder.data.api.order.SortOrder;
import io.devbench.uibuilder.data.collectionds.filter.CollectionFilterExpression;
import io.devbench.uibuilder.data.collectionds.filter.factories.CollectionFilterExpressionFactory;
import io.devbench.uibuilder.data.collectionds.interceptors.ItemDataSourceBindingContext;
import io.devbench.uibuilder.data.common.dataprovidersupport.KeyMapper;
import io.devbench.uibuilder.data.common.dataprovidersupport.OrmKeyMapper;
import io.devbench.uibuilder.data.common.dataprovidersupport.requestresponse.DataProviderRequest;
import io.devbench.uibuilder.data.common.datasource.CommonDataSource;
import io.devbench.uibuilder.data.common.datasource.PagingFetchRequest;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.annotation.Nonnull;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class CollectionDataSource extends CommonDataSource, ItemDataSourceBindingContext> {

    @Getter
    private final List keyPaths;
    private final KeyMapper keyMapper;
    private final CollectionFilterExpressionFactory filterExpressionFactory;
    private final Collection items;
    private Collection children;

    private CollectionOrderExpression orderExpression;
    private CollectionFilterExpression filterExpression;

    public CollectionDataSource(ItemDataSourceBindingContext bindings, List keyPaths, Collection items) {
        super(bindings);
        this.keyPaths = Collections.unmodifiableList(keyPaths);
        this.items = items;
        this.keyMapper = initKeyMapper(this.keyPaths, items);
        this.filterExpressionFactory = new CollectionFilterExpressionFactory<>();

        String sortPath = bindings.getSortPath();
        if (sortPath != null && !sortPath.trim().isEmpty()) {
            orderExpression = getOrderExpressionFactory().create();
            orderExpression.addOrder(new SortOrder(sortPath.trim(), bindings.getSortDirection()));
        }
    }

    private KeyMapper initKeyMapper(List keyPaths, Collection items) {
        if (keyPaths.isEmpty()) {
            return new GeneratedIdBasedKeyMapper<>(items);
        } else {
            return new OrmKeyMapper<>(this, getMetadataProvider(), this.keyPaths);
        }
    }

    public final Collection getItems() {
        if (children != null) {
            Collection children = this.children;
            this.children = null;
            return children;
        } else {
            return getRootItems();
        }
    }

    protected Collection getRootItems() {
        return this.items;
    }

    @Override
    @SuppressWarnings("unchecked")
    protected void handleHierarchicalRequest(DataProviderRequest request) {
        if (isHierarchical()) {
            Object o = getObjectFromRequest(request);
            if (o != null) {
                children = (Collection) getBindingContext().provideChildren(o);
            } else {
                children = null;
            }
        }
    }

    @Override
    public boolean hasChildren(ClassMetadata element) {
        return !getBindingContext().provideChildren(element.getInstance()).isEmpty();
    }

    @Override
    public boolean isHierarchical() {
        return getBindingContext().getHierarchyProvider() != null;
    }

    @Override
    public final T findElementByKeyFilter(@Nonnull @NotNull CollectionFilterExpression keyFilter) {
        return getItems()
            .stream()
            .filter(createPredicateIfNecessary(keyFilter))
            .findFirst()
            .orElse(null);
    }

    @Override
    @SuppressWarnings("unchecked")
    public final Class getElementType() {
        if (getItems().isEmpty()) {
            return (Class) Object.class;
        } else {
            return (Class) getItems().iterator().next().getClass();
        }
    }

    @Override
    public final FilterExpressionFactory> getFilterExpressionFactory() {
        return filterExpressionFactory;
    }

    @Override
    public final OrderExpressionFactory getOrderExpressionFactory() {
        return CollectionOrderExpression::new;
    }

    @Override
    public final void registerFilter(CollectionFilterExpression expression) {
        this.filterExpression = expression;
    }

    @Override
    public final void registerOrder(CollectionOrderExpression expression) {
        this.orderExpression = expression;
    }

    @Override
    public final long fetchSize(@Nullable PagingFetchRequest request) {
        return getItems().stream().filter(createPredicateIfNecessary(this.filterExpression)).count();
    }

    @Override
    public boolean hasChildren(CollectionFilterExpression request) {
        return false;
    }

    @Override
    public final List fetchData(@Nullable PagingFetchRequest request) {
        List filteredOrderedItems = getFilteredOrderedItems();
        if (isSourceOrdered() && isPagingFetchRequestPresent(request)) {
            int offset = request.getPage() * request.getPageSize();
            int end = offset + request.getPageSize();
            if (end > filteredOrderedItems.size()) {
                end = filteredOrderedItems.size();
            }
            return filteredOrderedItems.subList(offset, end);
        }
        return filteredOrderedItems;
    }

    private List getFilteredOrderedItems() {
        Comparator comparator = createGenericComparator();
        Predicate predicate = createPredicateIfNecessary(this.filterExpression);
        Stream stream = getItems().stream().filter(predicate);
        if (comparator != null) {
            stream = stream.sorted(comparator);
        }
        return stream.collect(Collectors.toList());
    }

    @NotNull
    private Predicate createPredicateIfNecessary(CollectionFilterExpression filterExpression) {
        if (filterExpression != null) {
            return filterExpression.toPredicate();
        } else {
            return t -> true;
        }
    }

    @Nullable
    private Comparator createGenericComparator() {
        CollectionOrderExpression orderExpression = this.orderExpression;
        if (orderExpression == null) {
            return null;
        } else {
            Comparator retComparator = null;
            for (SortOrder order : orderExpression.getOrders()) {
                Comparator current = createComparatorBasedOnOrder(order);
                if (retComparator == null) {
                    retComparator = current;
                } else {
                    retComparator = retComparator.thenComparing(current);
                }
            }
            return retComparator;
        }
    }

    private Comparator createComparatorBasedOnOrder(SortOrder sortOrder) {
        ClassMetadata classMetadata = ClassMetadata.ofClass(getElementType());
        @SuppressWarnings({"unchecked"})
        Comparator comparing = Comparator.comparing(item -> {
            try {
                String path = sortOrder.getPath();
                @SuppressWarnings("OptionalGetWithoutIsPresent")
                PropertyMetadata propertyMetadata = classMetadata.withInstance(item).property(path).get();
                return propertyMetadata.getInstance() == null ? null : (Comparable) propertyMetadata.getValue();
            } catch (Exception e) {
                throw new SortPathInvalidException(e);
            }
        }, Comparator.nullsLast(Comparable::compareTo));

        if (SortOrder.Direction.ASCENDING == sortOrder.getDirection()) {
            return comparing;
        } else {
            return comparing.reversed();
        }
    }

    @Override
    protected final KeyMapper createKeyMapper() {
        return this.keyMapper;
    }

    @Override
    public boolean isSourceOrdered() {
        return getItems() instanceof List || orderExpression != null;
    }

    @Override
    public Optional getIndexOfItem(T item) {
        if (isSourceOrdered()) {
            return Optional.of(getFilteredOrderedItems().indexOf(item));
        }
        return super.getIndexOfItem(item);
    }

    @Override
    public Optional> getIndexOfItems() {
        if (isSourceOrdered()) {
            List filteredOrderedItems = getFilteredOrderedItems();
            Map itemIndexMap = new HashMap<>();
            for (int index = 0; index < filteredOrderedItems.size(); index++) {
                itemIndexMap.put(filteredOrderedItems.get(index), index);
            }
            return Optional.of(itemIndexMap);
        }
        return super.getIndexOfItems();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy