Example usage for org.apache.commons.lang3.tuple Triple of

List of usage examples for org.apache.commons.lang3.tuple Triple of

Introduction

In this page you can find the example usage for org.apache.commons.lang3.tuple Triple of.

Prototype

public static <L, M, R> Triple<L, M, R> of(final L left, final M middle, final R right) 

Source Link

Document

Obtains an immutable triple of from three objects inferring the generic types.

This factory allows the triple to be created using inference to obtain the generic types.

Usage

From source file:de.pixida.logtest.designer.testrun.TestRunEditor.java

private List<Triple<String, Node, String>> createConfigurationForm() {
    final List<Triple<String, Node, String>> formItems = new ArrayList<>();

    // Automaton file
    final TextField automatonFilePath = new TextField();
    this.automatonFilePathProperty.bind(automatonFilePath.textProperty());
    HBox.setHgrow(automatonFilePath, Priority.ALWAYS);
    final Button selectAutomatonFilePathButton = SelectFileButton.createButtonWithFileSelection(
            automatonFilePath, Editor.Type.AUTOMATON.getIconName(), "Select " + Editor.Type.AUTOMATON.getName(),
            Editor.Type.AUTOMATON.getFileMask(), Editor.Type.AUTOMATON.getFileDescription());
    final HBox automatonFilePathConfig = new HBox(automatonFilePath, selectAutomatonFilePathButton);
    formItems.add(Triple.of("Automaton", automatonFilePathConfig, null));

    // Automaton parameters
    final TextArea parametersInputText = new TextArea();
    parametersInputText.setStyle("-fx-font-family: monospace");
    HBox.setHgrow(parametersInputText, Priority.ALWAYS);
    final int parametersInputLinesSize = 4;
    parametersInputText.setPrefRowCount(parametersInputLinesSize);
    this.parametersProperty.bind(parametersInputText.textProperty());
    formItems.add(Triple.of("Parameters", parametersInputText,
            "Set parameters for the execution. Each line can contain a parameter as follows: ${PARAMETER}=value, e.g. ${TIMEOUT}=10."));

    // Log file//from w w w  . j  a  va2s. c  om
    this.createLogFileSourceInputItems(formItems);

    // Log reader configuration file
    final TextField logReaderConfigurationFilePath = new TextField();
    this.logReaderConfigurationFilePathProperty.bind(logReaderConfigurationFilePath.textProperty());
    HBox.setHgrow(logReaderConfigurationFilePath, Priority.ALWAYS);
    final Button selectLogReaderConfigurationFilePathButton = SelectFileButton.createButtonWithFileSelection(
            logReaderConfigurationFilePath, Editor.Type.LOG_READER_CONFIG.getIconName(),
            "Select " + Editor.Type.LOG_READER_CONFIG.getName(), Editor.Type.LOG_READER_CONFIG.getFileMask(),
            Editor.Type.LOG_READER_CONFIG.getFileDescription());
    final HBox logReaderConfigurationFilePathConfig = new HBox(logReaderConfigurationFilePath,
            selectLogReaderConfigurationFilePathButton);
    formItems.add(Triple.of("Log Reader Configuration", logReaderConfigurationFilePathConfig, null));

    // Debug output
    final CheckBox cb = new CheckBox();
    this.showDebugOutputProperty.bind(cb.selectedProperty());
    formItems.add(Triple.of("Show Debug Output", cb,
            "Show verbose debug output. Might generate lots of text and slows down the"
                    + " evaluation, but is very helpful for debugging automatons while developing them."));

    return formItems;
}

From source file:alfio.manager.AdminReservationManager.java

private Result<Triple<TicketReservation, List<Ticket>, Event>> loadReservation(String reservationId) {
    return ticketReservationRepository.findOptionalReservationById(reservationId)
            .map(r -> Triple.of(r, ticketRepository.findTicketsInReservation(reservationId),
                    eventRepository.findByReservationId(reservationId)))
            .map(Result::success).orElseGet(() -> Result.error(ErrorCode.ReservationError.NOT_FOUND));
}

From source file:alfio.controller.ReservationController.java

@RequestMapping(value = "/event/{eventName}/reservation/{reservationId}/success", method = RequestMethod.GET)
public String showConfirmationPage(@PathVariable("eventName") String eventName,
        @PathVariable("reservationId") String reservationId,
        @RequestParam(value = "confirmation-email-sent", required = false, defaultValue = "false") boolean confirmationEmailSent,
        @RequestParam(value = "ticket-email-sent", required = false, defaultValue = "false") boolean ticketEmailSent,
        Model model, Locale locale, HttpServletRequest request) {

    return eventRepository.findOptionalByShortName(eventName).map(ev -> {
        Optional<TicketReservation> tr = ticketReservationManager.findById(reservationId);
        return tr.filter(r -> r.getStatus() == TicketReservationStatus.COMPLETE).map(reservation -> {
            SessionUtil.removeSpecialPriceData(request);
            model.addAttribute("reservationId", reservationId);
            model.addAttribute("reservation", reservation);
            model.addAttribute("confirmationEmailSent", confirmationEmailSent);
            model.addAttribute("ticketEmailSent", ticketEmailSent);

            List<Ticket> tickets = ticketReservationManager.findTicketsInReservation(reservationId);
            List<Triple<AdditionalService, List<AdditionalServiceText>, AdditionalServiceItem>> additionalServices = ticketReservationManager
                    .findAdditionalServicesInReservation(reservationId).stream()
                    .map(t -> Triple.of(
                            t.getLeft(), t.getMiddle().stream()
                                    .filter(d -> d.getLocale().equals(locale.getLanguage())).collect(toList()),
                            t.getRight()))
                    .collect(Collectors.toList());
            boolean hasPaidSupplement = ticketReservationManager.hasPaidSupplements(reservationId);
            model.addAttribute("ticketsByCategory", tickets.stream()
                    .collect(Collectors.groupingBy(Ticket::getCategoryId)).entrySet().stream().map((e) -> {
                        TicketCategory category = eventManager.getTicketCategoryById(e.getKey(), ev.getId());
                        List<TicketDecorator> decorators = TicketDecorator
                                .decorate(e.getValue(),
                                        !hasPaidSupplement && configurationManager.getBooleanConfigValue(
                                                Configuration.from(ev.getOrganizationId(), ev.getId(),
                                                        category.getId(), ALLOW_FREE_TICKETS_CANCELLATION),
                                                false),
                                        eventManager.checkTicketCancellationPrerequisites(),
                                        ticket -> ticketHelper.findTicketFieldConfigurationAndValue(ev.getId(),
                                                ticket, locale),
                                        tickets.size() == 1, TicketDecorator.EMPTY_PREFIX_GENERATOR);
                        return Pair.of(category, decorators);
                    }).collect(toList()));
            boolean ticketsAllAssigned = tickets.stream().allMatch(Ticket::getAssigned);
            model.addAttribute("ticketsAreAllAssigned", ticketsAllAssigned);
            model.addAttribute("collapseEnabled", tickets.size() > 1 && !ticketsAllAssigned);
            model.addAttribute("additionalServicesOnly", tickets.isEmpty() && !additionalServices.isEmpty());
            model.addAttribute("additionalServices", additionalServices);
            model.addAttribute("countries", TicketHelper.getLocalizedCountries(locale));
            model.addAttribute("pageTitle", "reservation-page-complete.header.title");
            model.addAttribute("event", ev);
            model.addAttribute("useFirstAndLastName", ev.mustUseFirstAndLastName());
            model.asMap().putIfAbsent("validationResult", ValidationResult.success());
            return "/event/reservation-page-complete";
        }).orElseGet(() -> redirectReservation(tr, eventName, reservationId));
    }).orElse("redirect:/");
}

From source file:alfio.manager.WaitingQueueManager.java

private Stream<Triple<WaitingQueueSubscription, TicketReservationWithOptionalCodeModification, ZonedDateTime>> distributeAvailableSeats(
        Event event, Ticket.TicketStatus status, Supplier<Integer> availableSeatSupplier) {
    int availableSeats = availableSeatSupplier.get();
    int eventId = event.getId();
    log.debug("processing {} subscribers from waiting queue", availableSeats);
    List<TicketCategory> unboundedCategories = ticketCategoryRepository
            .findUnboundedOrderByExpirationDesc(eventId);
    Iterator<Ticket> tickets = ticketRepository
            .selectWaitingTicketsForUpdate(eventId, status.name(), availableSeats).stream()
            .filter(t -> t.getCategoryId() != null || unboundedCategories.size() > 0).iterator();
    int expirationTimeout = configurationManager.getIntConfigValue(
            Configuration.from(event.getOrganizationId(), event.getId(), WAITING_QUEUE_RESERVATION_TIMEOUT), 4);
    ZonedDateTime expiration = ZonedDateTime.now(event.getZoneId()).plusHours(expirationTimeout)
            .with(WorkingDaysAdjusters.defaultWorkingDays());

    if (!tickets.hasNext()) {
        log.warn("Unable to assign tickets, returning an empty stream");
        return Stream.empty();
    }/*from w  w w.j  ava2  s  .co m*/
    return waitingQueueRepository.loadWaiting(eventId, availableSeats).stream()
            .map(wq -> Pair.of(wq, tickets.next())).map(pair -> {
                TicketReservationModification ticketReservation = new TicketReservationModification();
                ticketReservation.setAmount(1);
                Integer categoryId = Optional.ofNullable(pair.getValue().getCategoryId())
                        .orElseGet(() -> findBestCategory(unboundedCategories, pair.getKey())
                                .orElseThrow(RuntimeException::new).getId());
                ticketReservation.setTicketCategoryId(categoryId);
                return Pair.of(pair.getLeft(), new TicketReservationWithOptionalCodeModification(
                        ticketReservation, Optional.<SpecialPrice>empty()));
            }).map(pair -> Triple.of(pair.getKey(), pair.getValue(), expiration));
}

From source file:act.installer.HMDBParser.java

/**
 * Convert an HMDB XML document into a Chemical object.  Expects one chemical per document.
 * @param doc A parsed HMDB XML doc.//from   w ww .  ja  v a 2s .  c  o m
 * @return The corresponding chemical to store in the DB.
 * @throws JaxenException
 * @throws JSONException
 */
protected Chemical extractChemicalFromXMLDocument(Document doc) throws JaxenException, JSONException {
    String hmdbId = getText(HMDB_XPATH.HMDB_ID_TEXT, doc);

    String primaryName = getText(HMDB_XPATH.PRIMARY_NAME_TEXT, doc);
    String iupacName = getText(HMDB_XPATH.IUPAC_NAME_TEXT, doc);
    List<String> synonyms = getTextFromNodes(HMDB_XPATH.SYNONYMS_NODES, doc);

    String inchi = getText(HMDB_XPATH.INCHI_TEXT, doc);
    String smiles = getText(HMDB_XPATH.SMILES_TEXT, doc);

    // Require an InChI if we're going to consume this molecule.
    if (inchi == null || inchi.isEmpty()) {
        LOGGER.warn("No InChI found for HMDB chemical %s, aborting", hmdbId);
        return null;
    }

    String ontologyStatus = getText(HMDB_XPATH.ONTOLOGY_STATUS_TEXT, doc);
    List<String> ontologyOrigins = getTextFromNodes(HMDB_XPATH.ONTOLOGY_ORIGINS_NODES, doc);
    List<String> ontologyFunctions = getTextFromNodes(HMDB_XPATH.ONTOLOGY_FUNCTIONS_NODES, doc);
    List<String> ontologyApplications = getTextFromNodes(HMDB_XPATH.ONTOLOGY_APPLICATIONS_NODES, doc);
    List<String> ontologyLocations = getTextFromNodes(HMDB_XPATH.ONTOLOGY_LOCATIONS_NODES, doc);

    List<String> locationFluids = getTextFromNodes(HMDB_XPATH.LOCATIONS_FLUID_NODES, doc);
    List<String> locationTissues = getTextFromNodes(HMDB_XPATH.LOCATIONS_TISSUE_NODES, doc);

    List<String> pathwayNames = getTextFromNodes(HMDB_XPATH.PATHWAY_NAME_NODES, doc);

    List<String> diseaseNames = getTextFromNodes(HMDB_XPATH.DISEASE_NAME_NODES, doc);

    String metlinId = getText(HMDB_XPATH.METLIN_ID_TEXT, doc);
    String pubchemId = getText(HMDB_XPATH.PUBCHEM_ID_TEXT, doc);
    String chebiId = getText(HMDB_XPATH.CHEBI_ID_TEXT, doc);

    List<Node> proteins = getNodes(HMDB_XPATH.PROTEIN_L1_NODES, doc);
    // Simple triples of name, uniprot id, gene name.
    List<Triple<String, String, String>> proteinAttributes = new ArrayList<>(proteins.size());

    for (Node n : proteins) {
        /* In order to run XPath on a sub-document, we have to Extract the relevant nodes into their own document object.
         * If we try to run evaluate on `n` instead of this new document, we'll get matching paths for the original
         * document `d` but not for the nodes we're looking at right now.  Very weird. */
        Document proteinDoc = documentBuilder.newDocument();
        proteinDoc.adoptNode(n);
        proteinDoc.appendChild(n);
        String name = getText(HMDB_XPATH.PROTEIN_L2_NAME_TEXT, proteinDoc);
        String uniprotId = getText(HMDB_XPATH.PROTEIN_L2_UNIPROT_ID_TEXT, proteinDoc);
        String geneName = getText(HMDB_XPATH.PROTEIN_L2_GENE_NAME_TEXT, proteinDoc);
        proteinAttributes.add(Triple.of(name, uniprotId, geneName));
    }

    // Assumption: when we reach this point there will always be an InChI.
    Chemical chem = new Chemical(inchi);
    chem.setSmiles(smiles);

    chem.setCanon(primaryName);

    if (pubchemId != null && !pubchemId.isEmpty()) {
        chem.setPubchem(Long.valueOf(pubchemId));
    }

    synonyms.forEach(chem::addSynonym);
    chem.addSynonym(iupacName); // TODO: is there a better place for this?

    JSONObject meta = new JSONObject().put("hmdb_id", hmdbId).put("ontology",
            new JSONObject().put("status", ontologyStatus).put("origins", new JSONArray(ontologyOrigins))
                    .put("functions", new JSONArray(ontologyFunctions))
                    .put("applications", new JSONArray(ontologyApplications))
                    .put("locations", new JSONArray(ontologyLocations)))
            .put("location",
                    new JSONObject().put("fluid", new JSONArray(locationFluids)).put("tissue",
                            new JSONArray(locationTissues)))
            .put("pathway_names", new JSONArray(pathwayNames)).put("disease_names", new JSONArray(diseaseNames))
            .put("metlin_id", metlinId).put("chebi_id", chebiId).put("proteins",
                    new JSONArray(proteinAttributes
                            .stream().map(t -> new JSONObject().put("name", t.getLeft())
                                    .put("uniprot_id", t.getMiddle()).put("gene_name", t.getRight()))
                            .collect(Collectors.toList())));

    chem.putRef(Chemical.REFS.HMDB, meta);

    return chem;
}

From source file:at.gridtec.lambda4j.function.tri.TriFunction.java

/**
 * Returns a memoized (caching) version of this {@link TriFunction}. Whenever it is called, the mapping between the
 * input parameters and the return value is preserved in a cache, making subsequent calls returning the memoized
 * value instead of computing the return value again.
 * <p>//w  w w  . j a v a2s. com
 * Unless the function and therefore the used cache will be garbage-collected, it will keep all memoized values
 * forever.
 *
 * @return A memoized (caching) version of this {@code TriFunction}.
 * @implSpec This implementation does not allow the input parameters or return value to be {@code null} for the
 * resulting memoized function, as the cache used internally does not permit {@code null} keys or values.
 * @implNote The returned memoized function can be safely used concurrently from multiple threads which makes it
 * thread-safe.
 */
@Nonnull
default TriFunction<T, U, V, R> memoized() {
    if (isMemoized()) {
        return this;
    } else {
        final Map<Triple<T, U, V>, R> cache = new ConcurrentHashMap<>();
        final Object lock = new Object();
        return (TriFunction<T, U, V, R> & Memoized) (t, u, v) -> {
            final R returnValue;
            synchronized (lock) {
                returnValue = cache.computeIfAbsent(Triple.of(t, u, v),
                        key -> apply(key.getLeft(), key.getMiddle(), key.getRight()));
            }
            return returnValue;
        };
    }
}

From source file:hu.mta.sztaki.lpds.cloud.simulator.iaas.VirtualMachine.java

/**
 * Always use this function to set the current VM state. This way all the
 * interested parties get notified on the changes.
 * //w  ww.j av  a 2  s.  co  m
 * @param newstate
 *            The new state the VM is in.
 */
private void setState(final State newstate) {
    final State oldState = currState;
    currState = newstate;
    vmStateChangelistenerManager.notifyListeners(Triple.of(this, oldState, newstate));
}

From source file:com.nttec.everychan.ui.presentation.PresentationModel.java

private synchronized void updateViewModels(PostModel[] posts, boolean showIndex, CancellableTask task,
        RebuildCallback rebuildCallback) {
    if (task == null)
        task = CancellableTask.NOT_CANCELLABLE;
    synchronized (lock) {
        notReady = true;//ww w  .  j  a v a2s.  co  m
    }
    if (presentationList == null) {
        presentationList = new ArrayList<PresentationItemModel>(posts.length); //   ? ?(?)
        attachments = new ArrayList<Triple<AttachmentModel, String, String>>();
    }

    if (task.isCancelled())
        return;

    String[] subscriptions = null;
    if (source.pageModel.type == UrlPageModel.TYPE_THREADPAGE
            && MainApplication.getInstance().settings.highlightSubscriptions()) {
        subscriptions = MainApplication.getInstance().subscriptions.getSubscriptions(source.pageModel.chanName,
                source.pageModel.boardName, source.pageModel.threadNumber);
    }

    boolean headersRebuilding = false;
    int indexCounter = 0;

    boolean rebuild = false;
    if (posts.length < presentationList.size()) {
        rebuild = true;
        Logger.d(TAG, "rebuild: new list is shorter");
    } else {
        for (int i = 0, size = presentationList.size(); i < size; ++i) {
            if (!presentationList.get(i).sourceModel.number.equals(posts[i].number)
                    || ChanModels.hashPostModel(posts[i]) != presentationList.get(i).sourceModelHash) {
                rebuild = true;
                Logger.d(TAG, "rebuild: changed item " + i);
                break;
            }
            if (showIndex) {
                if (!posts[i].deleted)
                    ++indexCounter;
                if (headersRebuilding |= presentationList.get(i).isDeleted != posts[i].deleted) {
                    presentationList.get(i).buildSpannedHeader(!posts[i].deleted ? indexCounter : -1,
                            source.boardModel.bumpLimit, reduceNames ? source.boardModel.defaultUserName : null,
                            source.pageModel.type == UrlPageModel.TYPE_SEARCHPAGE ? posts[i].parentThread
                                    : null,
                            subscriptions != null ? Arrays.binarySearch(subscriptions, posts[i].number) >= 0
                                    : false);
                }
            }
            presentationList.get(i).isDeleted = posts[i].deleted;
        }
    }

    if (task.isCancelled())
        return;

    if (rebuild) {
        if (rebuildCallback != null)
            rebuildCallback.onRebuild();
        presentationList.clear();
        postNumbersMap.clear();
        attachments.clear();
        indexCounter = 0;
    }

    final boolean openSpoilers = MainApplication.getInstance().settings.openSpoilers();

    for (int i = presentationList.size(); i < posts.length; ++i) {
        if (task.isCancelled())
            return;
        PresentationItemModel model = new PresentationItemModel(posts[i], source.pageModel.chanName,
                source.pageModel.boardName,
                source.pageModel.type == UrlPageModel.TYPE_THREADPAGE ? source.pageModel.threadNumber : null,
                dateFormat, spanClickListener, imageGetter, ThemeUtils.ThemeColors.getInstance(theme),
                openSpoilers, floatingModels, subscriptions);
        postNumbersMap.put(posts[i].number, i);
        if (source.pageModel.type == UrlPageModel.TYPE_THREADPAGE) {
            for (String ref : model.referencesTo) {
                Integer postPosition = postNumbersMap.get(ref);
                if (postPosition != null && postPosition < presentationList.size()) {
                    presentationList.get(postPosition).addReferenceFrom(model.sourceModel.number);
                }
            }
        }
        presentationList.add(model);
        for (int j = 0; j < model.attachmentHashes.length; ++j) {
            attachments.add(Triple.of(posts[i].attachments[j], model.attachmentHashes[j], posts[i].number));
        }

        model.buildSpannedHeader(showIndex && !posts[i].deleted ? ++indexCounter : -1,
                source.boardModel.bumpLimit, reduceNames ? source.boardModel.defaultUserName : null,
                source.pageModel.type == UrlPageModel.TYPE_SEARCHPAGE ? posts[i].parentThread : null,
                subscriptions != null ? Arrays.binarySearch(subscriptions, posts[i].number) >= 0 : false);

        if (source.pageModel.type == UrlPageModel.TYPE_THREADPAGE) {
            model.hidden = isHiddenDelegate.isHidden(source.pageModel.chanName, source.pageModel.boardName,
                    source.pageModel.threadNumber, posts[i].number);
        } else if (source.pageModel.type == UrlPageModel.TYPE_BOARDPAGE
                || source.pageModel.type == UrlPageModel.TYPE_CATALOGPAGE) {
            model.hidden = isHiddenDelegate.isHidden(source.pageModel.chanName, source.pageModel.boardName,
                    posts[i].number, null);
        }
        if (!model.hidden && ( //?
        source.pageModel.type == UrlPageModel.TYPE_THREADPAGE
                || source.pageModel.type == UrlPageModel.TYPE_BOARDPAGE
                || source.pageModel.type == UrlPageModel.TYPE_CATALOGPAGE)) {
            for (AutohideActivity.CompiledAutohideRule rule : autohideRules) {
                if ((rule.inComment && model.spannedComment != null
                        && rule.pattern.matcher(model.spannedComment).find())
                        || (rule.inSubject && posts[i].subject != null
                                && rule.pattern.matcher(posts[i].subject).find())
                        || (rule.inName && (posts[i].name != null && rule.pattern.matcher(posts[i].name).find())
                                || (posts[i].trip != null && rule.pattern.matcher(posts[i].trip).find()))) {
                    model.hidden = true;
                    model.autohideReason = rule.regex;
                }
            }
        }
    }

    if (source.pageModel.type == UrlPageModel.TYPE_THREADPAGE) {
        for (PresentationItemModel model : presentationList) {
            if (task.isCancelled())
                return;
            model.buildReferencesString();
        }
    }

    if (source.threads != null) {
        for (int i = 0; i < source.threads.length; ++i) {
            if (task.isCancelled())
                return;
            presentationList.get(i).buildPostsCountString(source.threads[i].postsCount,
                    source.threads[i].attachmentsCount);
            presentationList.get(i).buildStickyClosedString(source.threads[i].isSticky,
                    source.threads[i].isClosed);
        }
    }
    notReady = false;
}

From source file:fr.landel.utils.assertor.helper.HelperAssertor.java

private static Triple<Boolean, EnumOperator, ResultAssertor> managesSub(final StepAssertor<?> step,
        final Object matcherObject, final boolean marcherMode, final List<ParameterAssertor<?>> parameters,
        final boolean valid, final EnumOperator operator, final MessagesAssertor messages,
        final boolean loadMessage) {

    final StepAssertor<?> subStep = step.getSubStep().get();
    final EnumOperator stepOperator = step.getOperator();
    EnumOperator nextOperator = operator;
    boolean nextValid = valid;

    final ResultAssertor subResult = HelperAssertor.combine(subStep, matcherObject, marcherMode, loadMessage);

    // in matcher mode, the matcher is not required, so we remove it
    final int size = subResult.getParameters().size();
    if (matcherObject != null && size > 1) {
        parameters.addAll(subResult.getParameters().subList(1, size));
    } else {//from   w  ww .  ja  v a2 s  . co m
        parameters.addAll(subResult.getParameters());
    }

    if (!subResult.isPrecondition()) {
        return Triple.of(false, null, subResult);
    } else {
        nextOperator = stepOperator;

        nextValid = HelperAssertor.isValid(nextValid, subResult.isValid(), nextOperator);

        if (!nextValid && loadMessage && subResult.getMessages() != null) {

            if (messages.isNotEmpty() && nextOperator != null) {
                messages.append(nextOperator);
            }

            messages.append(subResult.getMessages());
        }
    }

    return Triple.of(nextValid, nextOperator, null);
}

From source file:at.gridtec.lambda4j.function.tri.ThrowableTriFunction.java

/**
 * Returns a memoized (caching) version of this {@link ThrowableTriFunction}. Whenever it is called, the mapping
 * between the input parameters and the return value is preserved in a cache, making subsequent calls returning the
 * memoized value instead of computing the return value again.
 * <p>/*from  w ww .  j a va  2 s  .  c  om*/
 * Unless the function and therefore the used cache will be garbage-collected, it will keep all memoized values
 * forever.
 *
 * @return A memoized (caching) version of this {@code ThrowableTriFunction}.
 * @implSpec This implementation does not allow the input parameters or return value to be {@code null} for the
 * resulting memoized function, as the cache used internally does not permit {@code null} keys or values.
 * @implNote The returned memoized function can be safely used concurrently from multiple threads which makes it
 * thread-safe.
 */
@Nonnull
default ThrowableTriFunction<T, U, V, R, X> memoized() {
    if (isMemoized()) {
        return this;
    } else {
        final Map<Triple<T, U, V>, R> cache = new ConcurrentHashMap<>();
        final Object lock = new Object();
        return (ThrowableTriFunction<T, U, V, R, X> & Memoized) (t, u, v) -> {
            final R returnValue;
            synchronized (lock) {
                returnValue = cache.computeIfAbsent(Triple.of(t, u, v), ThrowableFunction
                        .of(key -> applyThrows(key.getLeft(), key.getMiddle(), key.getRight())));
            }
            return returnValue;
        };
    }
}