![JAR search and dependency download from the Maven repository](/logo.png)
io.takamaka.code.dao.SimpleSharedEntity Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of io-takamaka-code Show documentation
Show all versions of io-takamaka-code Show documentation
This module defines the support library of the Takamaka language.
The newest version!
/*
Copyright 2021 Fausto Spoto
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.takamaka.code.dao;
import static io.takamaka.code.lang.Takamaka.event;
import static io.takamaka.code.lang.Takamaka.require;
import static java.math.BigInteger.ZERO;
import java.math.BigInteger;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import io.takamaka.code.dao.SharedEntity.Offer;
import io.takamaka.code.lang.Exported;
import io.takamaka.code.lang.FromContract;
import io.takamaka.code.lang.Payable;
import io.takamaka.code.lang.PayableContract;
import io.takamaka.code.lang.Storage;
import io.takamaka.code.lang.View;
import io.takamaka.code.util.StorageMap;
import io.takamaka.code.util.StorageMapView;
import io.takamaka.code.util.StorageSet;
import io.takamaka.code.util.StorageSetView;
import io.takamaka.code.util.StorageTreeMap;
import io.takamaka.code.util.StorageTreeSet;
/**
* A simple implementation of a shared entity. Shareholders hold, sell and buy shares of a shared entity.
* Selling and buying shares do not require to pay a ticket.
*
* @param the type of the shareholders
* @param the type of the offers of sale of shares for this entity
*/
public class SimpleSharedEntity> extends PayableContract implements SharedEntity {
/**
* The shares of each shareholder. These are always positive.
*/
private final StorageMap shares = new StorageTreeMap<>();
/**
* The set of offers of sale of shares.
*/
private final StorageSet offers = new StorageTreeSet<>();
/**
* A snapshot of the current shares.
*/
private StorageMapView snapshotOfShares;
/**
* A snapshot of the current offers.
*/
private StorageSetView snapshotOfOffers;
/**
* Creates a shared entity with the given set of shareholders and respective shares.
*
* @param shareholders the initial shareholders; if there are repetitions, their shares are merged
* @param shares the initial shares of each initial shareholder. This must be as many as {@code shareholders}
*/
public SimpleSharedEntity(Stream shareholders, Stream shares) {
require(shareholders != null, "shareholders cannot be null");
require(shares != null, "shares cannot be null");
List shareholdersAsList = shareholders.collect(Collectors.toList());
List sharesAsList = shares.collect(Collectors.toList());
require(shareholdersAsList.size() == sharesAsList.size(), "shareholders and shares must have the same length");
Iterator it = sharesAsList.iterator();
for (S shareholder: shareholdersAsList) {
require(shareholder != null, "shareholders cannot be null");
BigInteger share = it.next();
require(share != null && share.signum() > 0, "shares must be positive big integers");
addShares(shareholder, share);
}
this.snapshotOfShares = this.shares.snapshot();
this.snapshotOfOffers = offers.snapshot();
}
/**
* Creates a shared entity with the given set of shareholders and respective shares.
*
* @param shareholders the initial shareholders; if there are repetitions, their shares are merged
* @param shares the initial shares of each initial shareholder. This must have the same length as {@code shareholders}
*/
public SimpleSharedEntity(S[] shareholders, BigInteger[] shares) {
this(Stream.of(shareholders), Stream.of(shares));
}
/**
* Creates a shared entity with one shareholder.
*
* @param shareholder the initial shareholder
* @param share the initial share of the initial shareholder
*/
public SimpleSharedEntity(S shareholder, BigInteger share) {
this(Stream.of(shareholder), Stream.of(share));
}
/**
* Creates a shared entity with two shareholders.
*
* @param shareholder1 the first initial shareholder
* @param shareholder2 the second initial shareholder
* @param share1 the initial share of the first shareholder
* @param share2 the initial share of the second shareholder
*/
public SimpleSharedEntity(S shareholder1, S shareholder2, BigInteger share1, BigInteger share2) {
this(Stream.of(shareholder1, shareholder2), Stream.of(share1, share2));
}
/**
* Creates a shared entity with three shareholders.
*
* @param shareholder1 the first initial shareholder
* @param shareholder2 the second initial shareholder
* @param shareholder3 the third initial shareholder
* @param share1 the initial share of the first shareholder
* @param share2 the initial share of the second shareholder
* @param share3 the initial share of the third shareholder
*/
public SimpleSharedEntity(S shareholder1, S shareholder2, S shareholder3, BigInteger share1, BigInteger share2, BigInteger share3) {
this(Stream.of(shareholder1, shareholder2, shareholder3), Stream.of(share1, share2, share3));
}
/**
* Creates a shared entity with four shareholders.
*
* @param shareholder1 the first initial shareholder
* @param shareholder2 the second initial shareholder
* @param shareholder3 the third initial shareholder
* @param shareholder4 the fourth initial shareholder
* @param share1 the initial share of the first shareholder
* @param share2 the initial share of the second shareholder
* @param share3 the initial share of the third shareholder
* @param share4 the initial share of the fourth shareholder
*/
public SimpleSharedEntity(S shareholder1, S shareholder2, S shareholder3, S shareholder4, BigInteger share1, BigInteger share2, BigInteger share3, BigInteger share4) {
this(Stream.of(shareholder1, shareholder2, shareholder3, shareholder4), Stream.of(share1, share2, share3, share4));
}
@Override
public @View final StorageSetView getOffers() {
return snapshotOfOffers;
}
@Override
public @View final StorageMapView getShares() {
return snapshotOfShares;
}
@Override
public @View BigInteger getTotalShares() {
return shares.values().reduce(BigInteger.ZERO, BigInteger::add);
}
@Override
public @View final boolean isShareholder(Object who) {
return snapshotOfShares.containsKey(who);
}
@Override
public final Stream getShareholders() {
return snapshotOfShares.keys();
}
@Override
public final @View BigInteger sharesOf(S shareholder) {
return shares.getOrDefault(shareholder, ZERO);
}
@Override
public final @View BigInteger sharesOnSaleOf(S shareholder) {
return offers.stream()
.filter(offer -> offer.seller == shareholder && offer.isOngoing())
.map(offer -> offer.sharesOnSale)
.reduce(ZERO, BigInteger::add);
}
@Override
public @FromContract(PayableContract.class) @Payable void place(BigInteger amount, O offer) {
require(offer.seller == caller(), "only the seller can place its own offer");
require(shares.containsKey(offer.seller), "the seller is not a shareholder");
require(sharesOf(offer.seller).subtract(sharesOnSaleOf(offer.seller)).compareTo(offer.sharesOnSale) >= 0, "the seller has not enough shares to sell");
cleanUpOffers(null);
offers.add(offer);
snapshotOfOffers = offers.snapshot();
event(new OfferPlaced<>(offer));
}
@Override
public @FromContract(PayableContract.class) @Payable void accept(BigInteger amount, S buyer, O offer) {
require(caller() == buyer, "only the future owner can buy the shares");
require(offers.contains(offer), "unknown offer");
require(offer.isOngoing(), "the sale offer is not ongoing anymore");
require(offer.cost.compareTo(amount) <= 0, "not enough money to accept the offer");
require(offer.buyer == null || buyer == offer.buyer, "the sale offer is reserved for another buyer");
cleanUpOffers(offer);
removeShares(offer.seller, offer.sharesOnSale);
addShares(buyer, offer.sharesOnSale);
offer.seller.receive(offer.cost);
snapshotOfShares = shares.snapshot();
snapshotOfOffers = offers.snapshot();
event(new OfferAccepted<>(buyer, offer));
}
@Override
public SharedEntityView view() {
@Exported
class SharedEntityViewImpl extends Storage implements SharedEntityView {
@Override @View
public StorageMapView getShares() {
return SimpleSharedEntity.this.getShares();
}
@Override
public Stream getShareholders() {
return SimpleSharedEntity.this.getShareholders();
}
@Override @View
public boolean isShareholder(Object who) {
return SimpleSharedEntity.this.isShareholder(who);
}
@Override @View
public BigInteger sharesOf(S shareholder) {
return SimpleSharedEntity.this.sharesOf(shareholder);
}
@Override @View
public BigInteger getTotalShares() {
return SimpleSharedEntity.this.getTotalShares();
}
@Override
public SharedEntityView snapshot() {
return SimpleSharedEntity.this.snapshot();
}
}
return new SharedEntityViewImpl();
}
@Override
public final SharedEntityView snapshot() {
@Exported
class SharedEntitySnapshotImpl extends Storage implements SharedEntityView {
/**
* Saves the shares at the time of creation of the snapshot.
*/
private final StorageMapView snapshotOfShares = SimpleSharedEntity.this.snapshotOfShares;
@Override @View
public StorageMapView getShares() {
return snapshotOfShares;
}
@Override
public Stream getShareholders() {
return snapshotOfShares.keys();
}
@Override @View
public boolean isShareholder(Object who) {
return snapshotOfShares.containsKey(who);
}
@Override @View
public BigInteger sharesOf(S shareholder) {
return snapshotOfShares.getOrDefault(shareholder, ZERO);
}
@Override @View
public BigInteger getTotalShares() {
return snapshotOfShares.values().reduce(BigInteger.ZERO, BigInteger::add);
}
@Override @View
public SharedEntityView snapshot() {
return this;
}
}
return new SharedEntitySnapshotImpl();
}
/**
* Removes a shareholder, distributing its shares to the other shareholders.
*
* @param toRemove the shareholder
*/
protected void removeShareholderAndDistributeToOthers(S toRemove) {
BigInteger toDistribute = sharesOf(toRemove);
shares.remove(toRemove);
event(new ShareholderRemoved<>(toRemove));
BigInteger total = shares.values().reduce(ZERO, BigInteger::add);
if (total.signum() > 0)
// TODO: avoid approximation: the last should get all the remaining shares
shares.keys().forEachOrdered(shareholder -> shares.update(shareholder, old -> old.add(toDistribute.multiply(old).divide(total))));
snapshotOfShares = shares.snapshot();
}
/**
* Removes some shares from a shareholder.
*
* @param shareholder the shareholder
* @param removed the shares to remove
*/
private void removeShares(S shareholder, BigInteger removed) {
shares.update(shareholder, old -> old.subtract(removed));
if (shares.get(shareholder).signum() == 0) {
shares.remove(shareholder);
event(new ShareholderRemoved<>(shareholder));
}
}
/**
* Deletes offers that have expired.
*
* @param offerToRemove an offer whose first occurrence must be removed
*/
private void cleanUpOffers(O offerToRemove) {
offers.stream()
.filter(offer -> offer == offerToRemove || !offer.isOngoing())
.forEachOrdered(offers::remove);
}
private void addShares(S shareholder, BigInteger share) {
shares.update(shareholder,
() -> {
event(new ShareholderAdded<>(shareholder));
return ZERO;
},
share::add);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy