
com.github.triceo.robozonky.app.portfolio.Portfolio Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2017 The RoboZonky Project
*
* 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 com.github.triceo.robozonky.app.portfolio;
import java.math.BigDecimal;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.SortedMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.github.triceo.robozonky.api.remote.entities.Investment;
import com.github.triceo.robozonky.api.remote.entities.Loan;
import com.github.triceo.robozonky.api.remote.enums.InvestmentStatus;
import com.github.triceo.robozonky.api.remote.enums.PaymentStatus;
import com.github.triceo.robozonky.api.remote.enums.PaymentStatuses;
import com.github.triceo.robozonky.api.strategies.PortfolioOverview;
import com.github.triceo.robozonky.app.util.DaemonRuntimeExceptionHandler;
import com.github.triceo.robozonky.common.remote.Zonky;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public enum Portfolio {
INSTANCE;
private static final Logger LOGGER = LoggerFactory.getLogger(Investment.class);
private final AtomicReference> investments = new AtomicReference<>(Collections.emptyList()),
investmentsPending = new AtomicReference<>(Collections.emptyList());
private final AtomicReference> loanCache = new AtomicReference<>(initSortedMap());
private final Map, Portfolio.UpdateType> updaters = new ConcurrentHashMap<>(0);
private final AtomicBoolean isUpdating = new AtomicBoolean(false);
private static SortedMap initSortedMap() {
return new ConcurrentSkipListMap<>();
}
public void registerUpdater(final Consumer updater) {
registerUpdater(updater, Portfolio.UpdateType.FULL);
}
public void registerUpdater(final Consumer updater, final Portfolio.UpdateType updateType) {
updaters.put(updater, updateType);
}
public void update(final Zonky zonky, final Portfolio.UpdateType updateType) {
if (this.isUpdating.getAndSet(true)) {
LOGGER.trace("Update ignored due to already being updated.");
return;
} else {
try {
LOGGER.trace("Update started: {}.", updateType);
if (updateType == Portfolio.UpdateType.FULL) {
loanCache.set(initSortedMap());
investments.set(zonky.getInvestments().collect(Collectors.toList()));
}
investmentsPending.set(Util.retrieveInvestmentsRepresentedByBlockedAmounts(zonky));
updaters.forEach((u, requiredType) -> {
if (requiredType == Portfolio.UpdateType.FULL && updateType == Portfolio.UpdateType.PARTIAL) {
return;
}
LOGGER.trace("Running dependent: {}", u);
u.accept(zonky);
});
LOGGER.trace("Finished.");
} catch (final Throwable t) { // users should know
new DaemonRuntimeExceptionHandler().handle(t);
} finally {
this.isUpdating.set(false);
}
}
}
public boolean isUpdating() {
return this.isUpdating.get();
}
public void update(final Zonky zonky) {
update(zonky, Portfolio.UpdateType.FULL);
}
public Stream getActiveWithPaymentStatus(final Set statuses) {
return getActive().filter(i -> statuses.stream().anyMatch(s -> Objects.equals(s, i.getPaymentStatus())));
}
public Stream getActiveWithPaymentStatus(final PaymentStatuses statuses) {
return getActiveWithPaymentStatus(statuses.getPaymentStatuses());
}
public Stream getActiveForSecondaryMarketplace() {
return getActive().filter(Investment::isCanBeOffered).filter(i -> !i.isOnSmp());
}
public Stream getActive() {
return investments.get().stream().filter(i -> i.getStatus() == InvestmentStatus.ACTIVE);
}
public Stream getPending() {
return investmentsPending.get().stream();
}
public PortfolioOverview calculateOverview(final BigDecimal balance) {
final Stream allInvestment =
Stream.concat(getActiveWithPaymentStatus(PaymentStatus.getActive()), getPending());
return PortfolioOverview.calculate(balance, allInvestment);
}
public PortfolioOverview calculateOverview(final Zonky zonky) {
return calculateOverview(zonky.getWallet().getAvailableBalance());
}
public Loan getLoan(final Zonky zonky, final int loanId) {
return loanCache.get().compute(loanId, (key, value) -> {
if (value != null) {
return value;
}
return zonky.getLoan(loanId);
});
}
public Optional getLoan(final int loanId) {
return Optional.ofNullable(loanCache.get().get(loanId));
}
/**
* For test purposes only.
*/
public void reset() {
investmentsPending.set(Collections.emptyList());
investments.set(Collections.emptyList());
loanCache.set(initSortedMap());
updaters.clear();
}
public enum UpdateType {
FULL,
PARTIAL
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy