de.metas.ui.web.handlingunits.HUEditorRowsPagedLoadingIterator.java Source code

Java tutorial

Introduction

Here is the source code for de.metas.ui.web.handlingunits.HUEditorRowsPagedLoadingIterator.java

Source

package de.metas.ui.web.handlingunits;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;

import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;

import de.metas.cache.CCache;
import de.metas.handlingunits.HuId;
import de.metas.ui.web.window.datatypes.DocumentId;
import de.metas.util.GuavaCollectors;
import de.metas.util.collections.IteratorUtils;
import lombok.Builder;
import lombok.NonNull;

/*
 * #%L
 * metasfresh-webui-api
 * %%
 * Copyright (C) 2017 metas GmbH
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 2 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program. If not, see
 * <http://www.gnu.org/licenses/gpl-2.0.html>.
 * #L%
 */

class HUEditorRowsPagedLoadingIterator implements Iterator<HUEditorRow> {
    private final int DEFAULT_BUFFERSIZE = 100;

    private final HUEditorViewRepository huEditorRepo;
    private final CCache<DocumentId, HUEditorRow> cache;
    private final int bufferSize;
    private final Iterator<HUEditorRowId> rowIds;
    private final HUEditorRowFilter filter;
    private final Predicate<HUEditorRow> filterPredicate;

    private Iterator<HUEditorRow> currentPageIterator;
    private boolean finished = false;

    @Builder
    private HUEditorRowsPagedLoadingIterator(@NonNull final HUEditorViewRepository huEditorRepo,
            @NonNull final CCache<DocumentId, HUEditorRow> cache, final int bufferSize,
            @NonNull final Iterator<HUEditorRowId> rowIds, final HUEditorRowFilter filter) {
        this.huEditorRepo = huEditorRepo;
        this.cache = cache;
        this.bufferSize = bufferSize > 0 ? bufferSize : DEFAULT_BUFFERSIZE;
        this.rowIds = rowIds;
        this.filter = filter != null ? filter : HUEditorRowFilter.ALL;
        this.filterPredicate = HUEditorRowFilters.toPredicate(this.filter);
    }

    @Override
    public boolean hasNext() {
        if (finished) {
            return false;
        }

        if (currentPageIterator == null || !currentPageIterator.hasNext()) {
            currentPageIterator = getNextPageIterator();
            if (!currentPageIterator.hasNext()) {
                finished = true;
                return false;
            } else {
                return true;
            }
        } else {
            return currentPageIterator.hasNext();
        }
    }

    @Override
    public HUEditorRow next() {
        if (currentPageIterator == null) {
            throw new NoSuchElementException();
        }

        return currentPageIterator.next();
    }

    public Stream<HUEditorRow> stream() {
        return IteratorUtils.stream(this);
    }

    private Iterator<HUEditorRow> getNextPageIterator() {
        // the result; part of it will be taken from cache, the, rest will be loaded
        final HUEditorRow[] rows = new HUEditorRow[bufferSize];

        // HUEditorRowIds that we don't have in the cache and that therefore need to be loaded
        final Map<HUEditorRowId, Integer> rowIdToLoad2index = new HashMap<>();

        // Get from cache as much as possible
        {
            int idx = 0;
            while (rowIds.hasNext() && idx < bufferSize) {
                final HUEditorRowId rowId = rowIds.next();

                final HUEditorRowId topLevelRowId = rowId.toTopLevelRowId();
                final HUEditorRow topLevelRow = cache.get(topLevelRowId.toDocumentId());

                if (topLevelRow == null) {
                    // to be loaded
                    rowIdToLoad2index.put(rowId, idx);
                } else {
                    if (rowId.equals(topLevelRowId)) {
                        rows[idx] = topLevelRow;
                    } else {
                        rows[idx] = topLevelRow.getIncludedRowById(rowId.toDocumentId()).orElse(null);
                    }
                }

                idx++;
            }
        }

        //
        // Load missing rows (which were not found in cache)
        if (!rowIdToLoad2index.isEmpty()) {
            final ListMultimap<HUEditorRowId, HUEditorRowId> topLevelRowId2rowIds = rowIdToLoad2index.keySet()
                    .stream().map(rowId -> GuavaCollectors.entry(rowId.toTopLevelRowId(), rowId))
                    .collect(GuavaCollectors.toImmutableListMultimap());

            final Set<HuId> topLevelHUIds = topLevelRowId2rowIds.keys().stream().map(HUEditorRowId::getTopLevelHUId)
                    .collect(ImmutableSet.toImmutableSet());

            huEditorRepo.retrieveHUEditorRows(topLevelHUIds, filter).forEach(topLevelRow -> {
                final HUEditorRowId topLevelRowId = topLevelRow.getHURowId();
                for (final HUEditorRowId includedRowId : topLevelRowId2rowIds.get(topLevelRowId)) {
                    final Integer idx = rowIdToLoad2index.remove(includedRowId);
                    if (idx == null) {
                        // wtf?! shall not happen
                        continue;
                    }

                    if (topLevelRowId.equals(includedRowId)) {
                        rows[idx] = topLevelRow;
                        cache.put(topLevelRow.getId(), topLevelRow);
                    } else {
                        rows[idx] = topLevelRow.getIncludedRowById(includedRowId.toDocumentId()).orElse(null);
                    }
                }
            });
        }

        return Stream.of(rows).filter(Predicates.notNull()) // IMPORTANT: just to make sure we won't stream some empty gaps (e.g. missing rows because HU was not a top level one)
                .filter(filterPredicate).iterator();
    }

}