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

com.almworks.jira.structure.api.row.RowRetriever Maven / Gradle / Ivy

The newest version!
package com.almworks.jira.structure.api.row;

import com.almworks.integers.*;
import com.almworks.jira.structure.api.forest.item.ItemForest;
import com.almworks.jira.structure.api.item.CoreIdentities;
import com.almworks.jira.structure.api.item.ItemIdentity;
import com.almworks.jira.structure.api.util.Ref;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.*;
import java.util.function.*;

import static com.almworks.jira.structure.api.row.ItemAccessMode.ITEM_NOT_NEEDED;
import static com.almworks.jira.structure.api.row.ItemAccessMode.NORMAL_ACCESS;

/**
 * 

{@code RowRetriever} is an abstraction of an object that can provide an instance of {@link StructureRow} by its numeric ID. * In addition, it has bulk access methods that may provide multiple {@code StructureRow} instances more efficiently.

* *

There's a number of bulk scanning methods that are provided for convenience. The following naming convention is used:

*
    *
  • {@link #scanRows} – basic scanning method with a {@link Predicate} callback, which allows breaking the iteration cycle.
  • *
  • {@link #scanAllRows} – "all" means that all passed rows will be scanned, and the callback is a {@link Consumer}.
  • *
  • {@link #scanAllExistingRows} – "existing" means that {@link #IGNORE_MISSING_ROWS} will be passed as the missing row collector, * which guarantees that the method will not throw {@link MissingRowException}.
  • *
* * @see RowManager * @see ItemForest */ public interface RowRetriever { /** * Use this to indicate that missing rows should not result in throwing {@link MissingRowException}. * * @see #scanRows(LongIterable, boolean, LongCollector, Predicate) */ LongCollector IGNORE_MISSING_ROWS = LongCollector.DUMMY; /** *

Retrieves information about a structure row by its ID.

* *

Note that it is a runtime error to request a non-existing row, because that should never happen in a * correct code.

* * @param rowId row ID * @return row information * @throws MissingRowException if the specified row ID does not exist */ @NotNull default StructureRow getRow(long rowId) throws MissingRowException { return getRow(rowId, NORMAL_ACCESS); } /** *

Retrieves {@code StructureRow} with additional information about how the calling code is going to use * method {@link StructureRow#getItem}. The implementation may be optimized according to the {@code access} parameter.

* *

If row's item is invisible or does not exist, {@code StructureRow} is returned anyway, but {@link StructureRow#getItem} * return {@code null}.

* * @param rowId row ID * @param access defines how item object is going to be accessed * @return row instance * @throws MissingRowException if the specified row ID does not exist * @see ItemAccessMode */ @NotNull StructureRow getRow(long rowId, @NotNull ItemAccessMode access) throws MissingRowException; /** *

Loads multiple rows by their IDs and calls {@code iteratee} with a {@link StructureRow} for each row ID in the input. * Use this method whenever you need to read a potentially large amount of rows and the order of retrieval is not important.

* *

For example, use {@code scanRows} to iterate through the whole forest to see if it has a generator row, * or to process user input that has an array of row IDs.

* *

The order in which {@code iteratee} is called is not guaranteed to be the same as the iteration order of {@code rows}. * Likewise, {@code missingCollector} may receive row IDs out of order.

* *

The implementation of {@code iteratee} must be reasonably fast and avoid taking locks or accessing long-running services. * It's possible to call other {@link RowRetriever} methods inside {@code iteratee}.

* *

It's possible to pass {@link LongIterator} as {@link LongIterable} - the iterator() method is guaranteed to be called only once.

* *

Item access mode

* *

You can choose an appropriate value for the {@code} access parameter based on what you're going to do with rows in the {@code iteratee}. * This allows the provider of {@code StructureRow} instances to optimize how {@link StructureRow#getItem} method works.

* *

Dealing with missing rows

* *

If a row ID in the input stream does not correspond to an existing row, the behavior depends on whether the {@code missingCollector} * parameter is set. If it's {@code null}, then the method will immediately throw a {@link MissingRowException}. If it's not null, the * collector will be called with the missing row ID.

* *

To simply ignore the missing rows, you can use {@link #IGNORE_MISSING_ROWS} collector or use one of the {@link #scanAllExistingRows} methods. * *

Note that if the current user does not have access to a row, but the row itself exists, it's not considered missing.

* *

Super-root

* *

Note that normally a row retriever does not handle {@link SuperRootRow} in any special way, so it will be a case of a missing row. Some * implementations may, however, support accessing the super-root.

* *

Implementation notes

* *

The default implementation is based on calling {@link #getRow}. Specific implementations should optimize this method for retrieving * multiple rows at once.

* *

Do not override other bulk scanning methods, unless that allows for additional optimization.

* * @param rows row IDs of the rows to scan * @param sorted if true, then the caller guarantees the row IDs to be sorted - this can be used for optimization * @param access indicates how the {@code getItem()} method of the provided {@code StructureRow} is going to be used and allows to skip the access checking * @param missingCollector a collector to receive non-existing row IDs, or {@code null} * @param iteratee predicate to call for each resolved row; if it returns {@code false}, the iteration stops and the method returns * @throws MissingRowException if a row was not found and {@code missingCollector} is null * @see RowManager * @see ItemAccessMode */ default void scanRows(@Nullable LongIterable rows, boolean sorted, @NotNull ItemAccessMode access, @Nullable LongCollector missingCollector, @NotNull Predicate iteratee) throws MissingRowException { if (rows != null) { for (LongIterator ii : rows) { try { StructureRow row = getRow(ii.value(), access); if (!iteratee.test(row)) break; } catch (MissingRowException e) { if (missingCollector == null) { throw e; } else { missingCollector.add(ii.value()); } } } } } /** * A convenience method that calls {@link #scanRows(LongIterable, boolean, ItemAccessMode, LongCollector, Predicate)} with * the normal access mode. * * @param rows row IDs of the rows to scan * @param sorted if true, then the caller guarantees the row IDs to be sorted - this can be used for optimization * @param missingCollector a collector to receive non-existing row IDs, or {@code null} * @param iteratee predicate to call for each resolved row; if it returns {@code false}, the iteration stops and the method returns * @throws MissingRowException if a row was not found and {@code missingCollector} is null */ default void scanRows(@Nullable LongIterable rows, boolean sorted, @Nullable LongCollector missingCollector, @NotNull Predicate iteratee) throws MissingRowException { scanRows(rows, sorted, NORMAL_ACCESS, missingCollector, iteratee); } /** * A convenience method that calls {@link #scanRows(LongIterable, boolean, ItemAccessMode, LongCollector, Predicate)} with * the normal access mode, and when the rows stream is not guaranteed to be sorted. * * @param rows row IDs of the rows to scan * @param missingCollector a collector to receive non-existing row IDs, or {@code null} * @param iteratee predicate to call for each resolved row; if it returns {@code false}, the iteration stops and the method returns * @throws MissingRowException if a row was not found and {@code missingCollector} is null */ default void scanRows(@Nullable LongIterable rows, @Nullable LongCollector missingCollector, @NotNull Predicate iteratee) throws MissingRowException { scanRows(rows, false, NORMAL_ACCESS, missingCollector, iteratee); } /** * A convenience method that calls {@link #scanRows(LongIterable, boolean, ItemAccessMode, LongCollector, Predicate)} with * the normal access mode, when the rows stream is not guaranteed to be sorted, and without a missing row collector. This method * will throw {@link MissingRowException} if a non-existent row ID is encountered. * * @param rows row IDs of the rows to scan * @param iteratee predicate to call for each resolved row; if it returns {@code false}, the iteration stops and the method returns * @throws MissingRowException if a row was not found */ default void scanRows(@Nullable LongIterable rows, @NotNull Predicate iteratee) throws MissingRowException { scanRows(rows, false, NORMAL_ACCESS, null, iteratee); } /** * A convenience variation of {@link #scanRows} that always goes through all of the row IDs. * Unlike {@code scanRows()} methods, it accepts a {@link Consumer} callback. * * @param rows row IDs of the rows to scan * @param sorted if true, then the caller guarantees the row IDs to be sorted - this can be used for optimization * @param access indicates how the {@code getItem()} method of the provided {@code StructureRow} is going to be used and allows to skip the access checking * @param missingCollector a collector to receive non-existing row IDs, or {@code null} * @param consumer a callback to call for every row * @throws MissingRowException if a row was not found and {@code missingCollector} is null */ default void scanAllRows(@Nullable LongIterable rows, boolean sorted, @NotNull ItemAccessMode access, @Nullable LongCollector missingCollector, @NotNull Consumer consumer) throws MissingRowException { scanRows(rows, sorted, access, missingCollector, row -> { consumer.accept(row); return true; }); } /** * A convenience variation of {@link #scanRows} that always goes through all of the row IDs with a normal access mode. * * @param rows row IDs of the rows to scan * @param sorted if true, then the caller guarantees the row IDs to be sorted - this can be used for optimization * @param missingCollector a collector to receive non-existing row IDs, or {@code null} * @param consumer a callback to call for every row * @throws MissingRowException if a row was not found and {@code missingCollector} is null */ default void scanAllRows(@Nullable LongIterable rows, boolean sorted, @Nullable LongCollector missingCollector, @NotNull Consumer consumer) throws MissingRowException { scanAllRows(rows, sorted, NORMAL_ACCESS, missingCollector, consumer); } /** * A convenience variation of {@link #scanRows} that always goes through all of the row IDs, uses normal access mode, and assumes that * row ID stream may be not sorted. * * @param rows row IDs of the rows to scan * @param missingCollector a collector to receive non-existing row IDs, or {@code null} * @param consumer a callback to call for every row * @throws MissingRowException if a row was not found and {@code missingCollector} is null */ default void scanAllRows(@Nullable LongIterable rows, @Nullable LongCollector missingCollector, @NotNull Consumer consumer) throws MissingRowException { scanAllRows(rows, false, missingCollector, consumer); } /** * A convenience variation of {@link #scanRows} that always goes through all of the row IDs, uses normal access mode, assumes that * row ID stream may be not sorted, and does not provide a missing collector. This method will throw {@link MissingRowException} * if a non-existent row ID is encountered. * * @param rows row IDs of the rows to scan * @param consumer a callback to call for every row * @throws MissingRowException if a row was not found */ default void scanAllRows(@Nullable LongIterable rows, @NotNull Consumer consumer) throws MissingRowException { scanAllRows(rows, null, consumer); } /** * A convenience variation of {@link #scanRows} that always goes through all of the row IDs and ignores any missing rows. * * @param rows row IDs of the rows to scan * @param sorted if true, then the caller guarantees the row IDs to be sorted - this can be used for optimization * @param access indicates how the {@code getItem()} method of the provided {@code StructureRow} is going to be used and allows to skip the access checking * @param consumer a callback to call for every row */ default void scanAllExistingRows(@Nullable LongIterable rows, boolean sorted, @NotNull ItemAccessMode access, @NotNull Consumer consumer) { scanAllRows(rows, sorted, access, IGNORE_MISSING_ROWS, consumer); } /** * A convenience variation of {@link #scanRows} that always goes through all of the row IDs with a normal access mode and ignores any missing rows. * * @param rows row IDs of the rows to scan * @param sorted if true, then the caller guarantees the row IDs to be sorted - this can be used for optimization * @param consumer a callback to call for every row */ default void scanAllExistingRows(@Nullable LongIterable rows, boolean sorted, @NotNull Consumer consumer) { scanAllExistingRows(rows, sorted, NORMAL_ACCESS, consumer); } /** * A convenience variation of {@link #scanRows} that always goes through all of the row IDs with a normal access mode, ignores any missing rows, * and assumes that the row ID stream may be not sorted. * * @param rows row IDs of the rows to scan * @param consumer a callback to call for every row */ default void scanAllExistingRows(@Nullable LongIterable rows, @NotNull Consumer consumer) { scanAllExistingRows(rows, false, consumer); } /** *

Performs a reduction over a collection of rows, identified by their IDs.

* *

The reducer function must be associative and commutative, because it may be called in a random order, not corresponding to the * iteration order of {@code rows}.

* * @param rows row IDs of the rows to scan * @param startingValue the initial value to start with * @param accumulator a function that receives the next row and the accumulated value, and then produces the next accumulated value * @param result type * @return the last accumulated value, or {@code startingValue} if there was no iteration * @throws MissingRowException if a row was not found */ @Nullable default T reduceOverRows(@Nullable LongIterable rows, @Nullable T startingValue, @NotNull BiFunction accumulator) throws MissingRowException { if (rows == null) { return startingValue; } Ref r = new Ref<>(startingValue); scanRows(rows, false, NORMAL_ACCESS, null, row -> { r.value = accumulator.apply(row, r.value); return true; }); return r.value; } /** *

Convenience method that can be used to collect all issue IDs from given row IDs. Ignores missing rows.

* * @param rows a collection of rows * @param sorted if true, then rows is sorted - can be used by the optimized code * @param issuesCollector a collection to receive issue IDs * @param type of the collector * @return the collector */ @NotNull default C collectIssueIds(@Nullable LongIterable rows, boolean sorted, @NotNull C issuesCollector) { if (rows == null) return issuesCollector; scanRows(rows, sorted, ITEM_NOT_NEEDED, IGNORE_MISSING_ROWS, row -> { ItemIdentity itemId = row.getItemId(); if (CoreIdentities.isIssue(itemId)) { issuesCollector.add(itemId.getLongId()); } return true; }); return issuesCollector; } /** *

Convenience method that can be used to collect all issue IDs from given row IDs. Ignores missing rows.

* * @param rows a collection of rows * @param issuesCollector a collection to receive issue IDs * @param type of the collector * @return the collector */ @NotNull default C collectIssueIds(@Nullable LongIterable rows, @NotNull C issuesCollector) { return collectIssueIds(rows, false, issuesCollector); } /** *

Convenience method that collects item IDs from row IDs. Ignores missing rows.

* *

The returned collection is owned by the caller and may be further modified.

* * @param rows a collection of rows * @return a collection of item IDs found in the input rows */ @NotNull Set collectItemIds(@Nullable LongIterable rows); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy