Create repository
This commit is contained in:
23
core/core.iml
Normal file
23
core/core.iml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false">
|
||||
<output url="file://$MODULE_DIR$/target/classes" />
|
||||
<output-test url="file://$MODULE_DIR$/target/test-classes" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.7" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: org.slf4j:slf4j-log4j12:1.7.7" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: log4j:log4j:1.2.17" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.11" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
|
||||
<orderEntry type="library" name="Maven: org.jetbrains:annotations:13.0" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
48
core/pom.xml
Normal file
48
core/pom.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>trader</groupId>
|
||||
<artifactId>Trader</artifactId>
|
||||
<version>1.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>core</artifactId>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
<artifactId>annotations</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
<build>
|
||||
<sourceDirectory>src/main/java</sourceDirectory>
|
||||
<testSourceDirectory>src/test/java</testSourceDirectory>
|
||||
<resources><resource><directory>src/main/resources</directory><filtering>false</filtering></resource></resources>
|
||||
<testResources><testResource><directory>src/test/resources</directory><filtering>false</filtering></testResource></testResources>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
3
core/src/main/java/META-INF/MANIFEST.MF
Normal file
3
core/src/main/java/META-INF/MANIFEST.MF
Normal file
@@ -0,0 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Class-Path: slf4j-api-1.7.5.jar
|
||||
|
||||
33
core/src/main/java/ru/trader/core/Item.java
Normal file
33
core/src/main/java/ru/trader/core/Item.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package ru.trader.core;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class Item implements Comparable<Item>{
|
||||
private String name;
|
||||
|
||||
public Item(String name) {
|
||||
setName(name);
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Item other) {
|
||||
Objects.requireNonNull(other, "Not compare with null");
|
||||
if (this == other) return 0;
|
||||
return name != null ? other.name != null ? name.compareTo(other.name) : -1 : 0;
|
||||
}
|
||||
}
|
||||
230
core/src/main/java/ru/trader/core/ItemStat.java
Normal file
230
core/src/main/java/ru/trader/core/ItemStat.java
Normal file
@@ -0,0 +1,230 @@
|
||||
package ru.trader.core;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.TreeSet;
|
||||
|
||||
public class ItemStat {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(ItemStat.class);
|
||||
|
||||
private final Item item;
|
||||
private final OFFER_TYPE type;
|
||||
private final TreeSet<Offer> offers;
|
||||
private double sum;
|
||||
private double avg;
|
||||
|
||||
|
||||
public ItemStat(Item item, OFFER_TYPE offerType) {
|
||||
this.offers = new TreeSet<>();
|
||||
this.item = item;
|
||||
this.type = offerType;
|
||||
this.sum = 0;
|
||||
this.avg = Double.NaN;
|
||||
}
|
||||
|
||||
void put(Offer offer){
|
||||
LOG.trace("Put offer {} to item stat {}", offer, this);
|
||||
assert offer.hasType(type) && offer.hasItem(item);
|
||||
if (offers.add(offer)){
|
||||
double price = offer.getPrice();
|
||||
sum += price;
|
||||
avg = sum / offers.size();
|
||||
LOG.trace("After this = {}", this);
|
||||
}
|
||||
}
|
||||
|
||||
void remove(Offer offer){
|
||||
LOG.trace("Remove offer {} from item stat {}", offer, this);
|
||||
assert offer.hasType(type) && offer.hasItem(item);
|
||||
if (offers.remove(offer)){
|
||||
if (offers.size()>0){
|
||||
double price = offer.getPrice();
|
||||
sum -= price;
|
||||
avg = sum / offers.size();
|
||||
} else {
|
||||
sum = 0; avg = Double.NaN;
|
||||
}
|
||||
LOG.trace("After this = {}", this);
|
||||
}
|
||||
}
|
||||
|
||||
void update(Offer offer, double price){
|
||||
LOG.trace("Update offer {} from item stat {}", offer, this);
|
||||
assert offer.hasType(type) && offer.hasItem(item) && offers.contains(offer);
|
||||
double oldPrice = offer.getPrice();
|
||||
offers.remove(offer);
|
||||
offer.setPrice(price);
|
||||
offers.add(offer);
|
||||
sum += price - oldPrice;
|
||||
avg = sum / offers.size();
|
||||
LOG.trace("After update this = {}", this);
|
||||
}
|
||||
|
||||
public OFFER_TYPE getType(){
|
||||
return type;
|
||||
}
|
||||
|
||||
public Item getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
public double getAvg(){
|
||||
return avg;
|
||||
}
|
||||
|
||||
public Offer getBest() {
|
||||
if (offers.isEmpty()) return getFake();
|
||||
return type.getOrder() > 0 ? offers.first() : offers.last();
|
||||
}
|
||||
|
||||
public int getOffersCount(){
|
||||
return offers.size();
|
||||
}
|
||||
|
||||
public Collection<Offer> getOffers() {
|
||||
return Collections.unmodifiableCollection(offers);
|
||||
}
|
||||
|
||||
public Offer getMin() {
|
||||
if (offers.isEmpty()) return getFake();
|
||||
return offers.first();
|
||||
}
|
||||
|
||||
public Offer getMax() {
|
||||
if (offers.isEmpty()) return getFake();
|
||||
return offers.last();
|
||||
}
|
||||
|
||||
public boolean isEmpty(){
|
||||
return offers.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("{");
|
||||
sb.append(item);
|
||||
sb.append(", ").append(type);
|
||||
if (LOG.isTraceEnabled()){
|
||||
sb.append(", count=").append(offers.size());
|
||||
sb.append(", sum=").append(sum);
|
||||
}
|
||||
sb.append(", avg=").append(avg);
|
||||
sb.append(", best=").append(getBest());
|
||||
sb.append(", min=").append(getMin());
|
||||
sb.append(", max=").append(getMax());
|
||||
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
private Offer getFake() {
|
||||
return new Offer() {
|
||||
|
||||
@Override
|
||||
public Item getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OFFER_TYPE getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getPrice() {
|
||||
return Double.NaN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vendor getVendor() {
|
||||
return NONE_VENDOR;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasType(OFFER_TYPE offerType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasItem(Item item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
void setPrice(double price) {
|
||||
throw new UnsupportedOperationException("Is fake offer, change unsupported");
|
||||
}
|
||||
|
||||
@Override
|
||||
void setVendor(Vendor vendor) {
|
||||
throw new UnsupportedOperationException("Is fake offer, change unsupported");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static Vendor NONE_VENDOR = new Vendor() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "None";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Offer> getOffers() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Item> getItems(OFFER_TYPE offerType) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Offer getOffer(OFFER_TYPE offerType, Item item) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasOffer(OFFER_TYPE offerType, Item item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addOffer(Offer offer) {
|
||||
throw new UnsupportedOperationException("Is fake vendor, change unsupported");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeOffer(Offer offer) {
|
||||
throw new UnsupportedOperationException("Is fake vendor, change unsupported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setName(String name) {
|
||||
throw new UnsupportedOperationException("Is fake vendor, change unsupported");
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof ItemStat)) return false;
|
||||
|
||||
ItemStat itemStat = (ItemStat) o;
|
||||
|
||||
return type == itemStat.type && item.equals(itemStat.item);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = item.hashCode();
|
||||
result = 31 * result + type.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
44
core/src/main/java/ru/trader/core/Market.java
Normal file
44
core/src/main/java/ru/trader/core/Market.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package ru.trader.core;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
public interface Market {
|
||||
boolean isChange();
|
||||
|
||||
ItemStat getStat(Offer offer);
|
||||
|
||||
ItemStat getStat(OFFER_TYPE type, Item item);
|
||||
|
||||
ItemStat getStatSell(Item item);
|
||||
|
||||
ItemStat getStatBuy(Item item);
|
||||
|
||||
Collection<Offer> getSell(Item item);
|
||||
|
||||
Collection<Offer> getBuy(Item item);
|
||||
|
||||
void add(Vendor vendor);
|
||||
|
||||
void add(Item item);
|
||||
|
||||
void remove(Vendor vendor);
|
||||
|
||||
void remove(Item item);
|
||||
|
||||
Collection<Vendor> get();
|
||||
|
||||
void add(Vendor vendor, Offer offer);
|
||||
|
||||
void remove(Vendor vendor, Offer offer);
|
||||
|
||||
Collection<Item> getItems();
|
||||
|
||||
void addVendors(Collection<? extends Vendor> vendors);
|
||||
|
||||
void addItems(Collection<? extends Item> items);
|
||||
|
||||
void updatePrice(Offer offer, double price);
|
||||
|
||||
void setChange(boolean change);
|
||||
}
|
||||
152
core/src/main/java/ru/trader/core/MarketSupport.java
Normal file
152
core/src/main/java/ru/trader/core/MarketSupport.java
Normal file
@@ -0,0 +1,152 @@
|
||||
package ru.trader.core;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class MarketSupport implements Market {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(MarketSupport.class);
|
||||
|
||||
protected abstract void addVendor(Vendor vendor);
|
||||
protected abstract void removeVendor(Vendor vendor);
|
||||
protected abstract void addItem(Item item);
|
||||
protected abstract void removeItem(Item item);
|
||||
protected abstract Collection<Vendor> getVendors();
|
||||
protected abstract Collection<Item> getItemList();
|
||||
|
||||
@Override
|
||||
public abstract ItemStat getStat(OFFER_TYPE offerType, Item item);
|
||||
|
||||
private boolean change;
|
||||
|
||||
@Override
|
||||
public boolean isChange() {
|
||||
return change;
|
||||
}
|
||||
|
||||
protected Collection<Vendor> getVendors(OFFER_TYPE offerType, Item item){
|
||||
List<Vendor> offers = getVendors()
|
||||
.stream()
|
||||
.filter(vendor -> vendor.hasOffer(offerType, item))
|
||||
.collect(Collectors.toList());
|
||||
return Collections.unmodifiableCollection(offers);
|
||||
}
|
||||
|
||||
protected Collection<Offer> getOffers(OFFER_TYPE offerType, Item item){
|
||||
ItemStat entry = getStat(offerType, item);
|
||||
return entry!=null ? Collections.unmodifiableCollection(entry.getOffers()) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ItemStat getStat(Offer offer){
|
||||
return getStat(offer.getType(), offer.getItem());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ItemStat getStatSell(Item item){
|
||||
return getStat(OFFER_TYPE.SELL, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final ItemStat getStatBuy(Item item){
|
||||
return getStat(OFFER_TYPE.BUY, item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Collection<Offer> getSell(Item item){
|
||||
return getOffers(OFFER_TYPE.SELL,item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Collection<Offer> getBuy(Item item){
|
||||
return getOffers(OFFER_TYPE.BUY,item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void add(Vendor vendor){
|
||||
LOG.debug("Add vendor {} to market {}", vendor, this);
|
||||
change = true;
|
||||
addVendor(vendor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void add(Item item){
|
||||
LOG.debug("Add item {} to market {}", item, this);
|
||||
change = true;
|
||||
addItem(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void remove(Vendor vendor){
|
||||
LOG.debug("Remove vendor {} from market {}", vendor, this);
|
||||
change = true;
|
||||
removeVendor(vendor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void remove(Item item){
|
||||
LOG.debug("Remove item {} from market {}", item, this);
|
||||
change = true;
|
||||
removeItem(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Collection<Vendor> get(){
|
||||
return Collections.unmodifiableCollection(getVendors());
|
||||
}
|
||||
|
||||
// Execute on add or remove offer
|
||||
protected void onAdd(Offer offer){}
|
||||
protected void onRemove(Offer offer){}
|
||||
|
||||
@Override
|
||||
public final void add(Vendor vendor, Offer offer){
|
||||
LOG.debug("Add offer {} to vendor {}", offer, vendor);
|
||||
change = true;
|
||||
vendor.add(offer);
|
||||
onAdd(offer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void remove(Vendor vendor, Offer offer){
|
||||
LOG.debug("Remove offer {} from vendor {}", offer, vendor);
|
||||
change = true;
|
||||
vendor.remove(offer);
|
||||
onRemove(offer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Collection<Item> getItems(){
|
||||
return Collections.unmodifiableCollection(getItemList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addVendors(Collection<? extends Vendor> vendors) {
|
||||
change = true;
|
||||
for (Vendor vendor : vendors) {
|
||||
add(vendor);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addItems(Collection<? extends Item> items) {
|
||||
change = true;
|
||||
for (Item item : items) {
|
||||
add(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePrice(Offer offer, double price){
|
||||
change = true;
|
||||
getStat(offer).update(offer, price);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChange(boolean change) {
|
||||
this.change = change;
|
||||
}
|
||||
}
|
||||
|
||||
17
core/src/main/java/ru/trader/core/OFFER_TYPE.java
Normal file
17
core/src/main/java/ru/trader/core/OFFER_TYPE.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package ru.trader.core;
|
||||
|
||||
|
||||
public enum OFFER_TYPE {
|
||||
SELL,
|
||||
BUY {
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
public int getOrder(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
107
core/src/main/java/ru/trader/core/Offer.java
Normal file
107
core/src/main/java/ru/trader/core/Offer.java
Normal file
@@ -0,0 +1,107 @@
|
||||
package ru.trader.core;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class Offer implements Comparable<Offer>{
|
||||
private final static Logger LOG = LoggerFactory.getLogger(Offer.class);
|
||||
|
||||
private Vendor vendor;
|
||||
private final Item item;
|
||||
private final OFFER_TYPE type;
|
||||
private double price;
|
||||
|
||||
Offer(){
|
||||
item = null;
|
||||
type = null;
|
||||
}
|
||||
|
||||
public Offer(OFFER_TYPE type, Item item, double price) {
|
||||
this.item = item;
|
||||
this.type = type;
|
||||
setPrice(price);
|
||||
}
|
||||
|
||||
public Item getItem() {
|
||||
return item;
|
||||
}
|
||||
|
||||
public OFFER_TYPE getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public double getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
void setPrice(double price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public Vendor getVendor() {
|
||||
return vendor;
|
||||
}
|
||||
|
||||
void setVendor(Vendor vendor) {
|
||||
LOG.trace("Set vendor {} to item {}", vendor, this);
|
||||
this.vendor = vendor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder sb = new StringBuilder("{");
|
||||
sb.append("").append(vendor);
|
||||
sb.append(", ").append(item);
|
||||
sb.append(", ").append(type);
|
||||
if (LOG.isTraceEnabled()){
|
||||
sb.append(", ").append(price);
|
||||
} else {
|
||||
sb.append(", ").append(getPrice());
|
||||
}
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public boolean hasType(OFFER_TYPE offerType) {
|
||||
return this.type.equals(offerType);
|
||||
}
|
||||
|
||||
public boolean hasItem(Item item) {
|
||||
return this.item.equals(item);
|
||||
}
|
||||
|
||||
public boolean equalsPrice(Offer offer){
|
||||
return equalsType(offer) && price==offer.price;
|
||||
}
|
||||
|
||||
public boolean equalsType(Offer offer){
|
||||
return offer!=null &&
|
||||
type.equals(offer.type) &&
|
||||
item.equals(offer.item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Offer other) {
|
||||
Objects.requireNonNull(other, "Not compare with null");
|
||||
if (this == other) return 0;
|
||||
int cmp = type.compareTo(other.type);
|
||||
if (cmp!=0) return cmp;
|
||||
cmp = Double.compare(price, other.price);
|
||||
if (cmp!=0) return cmp;
|
||||
cmp = vendor.compareTo(other.vendor);
|
||||
if (cmp!=0) return cmp;
|
||||
return item.compareTo(other.item);
|
||||
}
|
||||
|
||||
public String toVString(){
|
||||
return String.format("%s (%.0f)", getVendor().getName(), price);
|
||||
}
|
||||
|
||||
public String toIString(){
|
||||
return String.format("%s (%.0f)", item.getName(), price);
|
||||
}
|
||||
|
||||
}
|
||||
141
core/src/main/java/ru/trader/core/SimpleMarket.java
Normal file
141
core/src/main/java/ru/trader/core/SimpleMarket.java
Normal file
@@ -0,0 +1,141 @@
|
||||
package ru.trader.core;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SimpleMarket extends MarketSupport {
|
||||
protected Set<Vendor> vendors;
|
||||
protected List<Item> items;
|
||||
|
||||
//caching
|
||||
private final Map<Item,ItemStat> sellItems = new HashMap<>();
|
||||
private final Map<Item,ItemStat> buyItems = new HashMap<>();
|
||||
|
||||
public SimpleMarket() {
|
||||
init();
|
||||
}
|
||||
|
||||
protected void init() {
|
||||
vendors = new TreeSet<>();
|
||||
items = new ArrayList<>();
|
||||
}
|
||||
|
||||
private Map<Item,ItemStat> getItemCache(OFFER_TYPE offerType){
|
||||
switch (offerType) {
|
||||
case SELL: return sellItems;
|
||||
case BUY: return buyItems;
|
||||
default:
|
||||
throw new IllegalArgumentException("Wrong offer type: "+offerType);
|
||||
}
|
||||
}
|
||||
|
||||
private void put(Map<Item, ItemStat> cache, Offer offer){
|
||||
Item item = offer.getItem();
|
||||
ItemStat entry = cache.get(item);
|
||||
if (entry==null){
|
||||
entry = newItemStat(item, offer.getType());
|
||||
cache.put(item, entry);
|
||||
}
|
||||
entry.put(offer);
|
||||
}
|
||||
|
||||
protected ItemStat newItemStat(Item item, OFFER_TYPE offerType){
|
||||
return new ItemStat(item, offerType);
|
||||
}
|
||||
|
||||
private void remove(Map<Item, ItemStat> cache, Offer offer){
|
||||
Item item = offer.getItem();
|
||||
ItemStat entry = cache.get(item);
|
||||
if (entry!=null){
|
||||
entry.remove(offer);
|
||||
if (entry.getOffersCount()==0)
|
||||
cache.remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addVendor(Vendor vendor) {
|
||||
vendors.add(vendor);
|
||||
Collection<Offer> offers = vendor.getAllSellOffers();
|
||||
for (Offer offer : offers) {
|
||||
put(sellItems, offer);
|
||||
}
|
||||
offers = vendor.getAllBuyOffers();
|
||||
for (Offer offer : offers) {
|
||||
put(buyItems, offer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeVendor(Vendor vendor) {
|
||||
vendors.remove(vendor);
|
||||
Collection<Offer> offers = vendor.getAllSellOffers();
|
||||
for (Offer offer : offers) {
|
||||
remove(sellItems, offer);
|
||||
}
|
||||
offers = vendor.getAllBuyOffers();
|
||||
for (Offer offer : offers) {
|
||||
remove(buyItems, offer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addItem(Item item) {
|
||||
if (!items.contains(item))
|
||||
items.add(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeItem(Item item) {
|
||||
items.remove(item);
|
||||
sellItems.remove(item);
|
||||
buyItems.remove(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Vendor> getVendors() {
|
||||
return vendors;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Vendor> getVendors(OFFER_TYPE offerType, Item item) {
|
||||
List<Vendor> result = null;
|
||||
ItemStat entry = getItemCache(offerType).get(item);
|
||||
if (entry!=null){
|
||||
result = entry.getOffers()
|
||||
.stream()
|
||||
.map(Offer::getVendor)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return result!=null ? Collections.unmodifiableCollection(result) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStat getStat(OFFER_TYPE offerType, Item item) {
|
||||
ItemStat entry = getItemCache(offerType).get(item);
|
||||
return entry!=null ? entry : newItemStat(item, offerType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Item> getItemList() {
|
||||
return items;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onAdd(Offer offer) {
|
||||
put(getItemCache(offer.getType()), offer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRemove(Offer offer) {
|
||||
remove(getItemCache(offer.getType()), offer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addItems(Collection<? extends Item> items) {
|
||||
this.items.addAll(items);
|
||||
}
|
||||
|
||||
}
|
||||
88
core/src/main/java/ru/trader/core/SimpleVendor.java
Normal file
88
core/src/main/java/ru/trader/core/SimpleVendor.java
Normal file
@@ -0,0 +1,88 @@
|
||||
package ru.trader.core;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class SimpleVendor extends Vendor {
|
||||
|
||||
protected Map<Item, Offer> sell;
|
||||
protected Map<Item, Offer> buy;
|
||||
|
||||
public SimpleVendor() {
|
||||
super();
|
||||
initOffers();
|
||||
}
|
||||
|
||||
public SimpleVendor(String name) {
|
||||
super(name);
|
||||
initOffers();
|
||||
}
|
||||
|
||||
protected void initOffers(){
|
||||
sell = new HashMap<>();
|
||||
buy = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Offer> getOffers(OFFER_TYPE offerType) {
|
||||
switch (offerType) {
|
||||
case SELL: return sell.values();
|
||||
case BUY: return buy.values();
|
||||
}
|
||||
throw new IllegalArgumentException("Wrong offer type: "+offerType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Offer> getOffers() {
|
||||
ArrayList<Offer> offers = new ArrayList<>(sell.values());
|
||||
offers.addAll(buy.values());
|
||||
return offers;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<Item> getItems(OFFER_TYPE offerType) {
|
||||
switch (offerType) {
|
||||
case SELL: return sell.keySet();
|
||||
case BUY: return buy.keySet();
|
||||
}
|
||||
throw new IllegalArgumentException("Wrong offer type: "+offerType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Offer getOffer(OFFER_TYPE offerType, Item item) {
|
||||
switch (offerType) {
|
||||
case SELL: return sell.get(item);
|
||||
case BUY: return buy.get(item);
|
||||
}
|
||||
throw new IllegalArgumentException("Wrong offer type: "+offerType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasOffer(OFFER_TYPE offerType, Item item) {
|
||||
switch (offerType) {
|
||||
case SELL: return sell.containsKey(item);
|
||||
case BUY: return buy.containsKey(item);
|
||||
}
|
||||
throw new IllegalArgumentException("Wrong offer type: "+offerType);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addOffer(Offer offer) {
|
||||
switch (offer.getType()) {
|
||||
case SELL: sell.put(offer.getItem(), offer);
|
||||
break;
|
||||
case BUY: buy.put(offer.getItem(), offer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeOffer(Offer offer) {
|
||||
switch (offer.getType()) {
|
||||
case SELL: sell.remove(offer.getItem(), offer);
|
||||
break;
|
||||
case BUY: buy.remove(offer.getItem(), offer);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
109
core/src/main/java/ru/trader/core/Vendor.java
Normal file
109
core/src/main/java/ru/trader/core/Vendor.java
Normal file
@@ -0,0 +1,109 @@
|
||||
package ru.trader.core;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class Vendor implements Comparable<Vendor> {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(Vendor.class);
|
||||
|
||||
private String name;
|
||||
|
||||
protected abstract Collection<Offer> getOffers();
|
||||
protected abstract Collection<Item> getItems(OFFER_TYPE offerType);
|
||||
protected abstract Offer getOffer(OFFER_TYPE offerType, Item item);
|
||||
protected abstract boolean hasOffer(OFFER_TYPE offerType, Item item);
|
||||
protected abstract void addOffer(Offer offer);
|
||||
protected abstract void removeOffer(Offer offer);
|
||||
|
||||
protected Vendor() {
|
||||
}
|
||||
|
||||
protected Vendor(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
protected Collection<Offer> getOffers(OFFER_TYPE offerType){
|
||||
List<Offer> offers = getOffers()
|
||||
.stream()
|
||||
.filter(offer -> offer.hasType(offerType))
|
||||
.collect(Collectors.toList());
|
||||
return Collections.unmodifiableCollection(offers);
|
||||
}
|
||||
|
||||
public final Collection<Offer> getAllOffers(){
|
||||
return Collections.unmodifiableCollection(getOffers());
|
||||
}
|
||||
|
||||
public final Collection<Offer> getAllSellOffers(){
|
||||
return Collections.unmodifiableCollection(getOffers(OFFER_TYPE.SELL));
|
||||
}
|
||||
|
||||
public final Collection<Offer> getAllBuyOffers(){
|
||||
return Collections.unmodifiableCollection(getOffers(OFFER_TYPE.BUY));
|
||||
}
|
||||
|
||||
public final Offer getSell(Item item){
|
||||
return getOffer(OFFER_TYPE.SELL, item);
|
||||
}
|
||||
|
||||
public final Offer getBuy(Item item){
|
||||
return getOffer(OFFER_TYPE.BUY, item);
|
||||
}
|
||||
|
||||
public final boolean hasSell(Item item){
|
||||
return hasOffer(OFFER_TYPE.SELL, item);
|
||||
}
|
||||
|
||||
public final boolean hasBuy(Item item){
|
||||
return hasOffer(OFFER_TYPE.BUY, item);
|
||||
}
|
||||
|
||||
public final void add(Offer offer){
|
||||
LOG.trace("Add offer {} to vendor {}", offer, this);
|
||||
offer.setVendor(this);
|
||||
addOffer(offer);
|
||||
|
||||
}
|
||||
|
||||
public final void remove(Offer offer){
|
||||
LOG.trace("Remove offer {} from vendor {}", offer, this);
|
||||
assert this.equals(offer.getVendor());
|
||||
removeOffer(offer);
|
||||
}
|
||||
|
||||
|
||||
public final Collection<Item> getSellItems() {
|
||||
return getItems(OFFER_TYPE.SELL);
|
||||
}
|
||||
|
||||
public final Collection<Item> getBuyItems() {
|
||||
return getItems(OFFER_TYPE.BUY);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull Vendor other) {
|
||||
Objects.requireNonNull(other, "Not compare with null");
|
||||
if (this == other) return 0;
|
||||
return name != null ? other.name != null ? name.compareTo(other.name) : -1 : 0;
|
||||
}
|
||||
}
|
||||
120
core/src/main/java/ru/trader/store/MarketDocHandler.java
Normal file
120
core/src/main/java/ru/trader/store/MarketDocHandler.java
Normal file
@@ -0,0 +1,120 @@
|
||||
package ru.trader.store;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.SAXParseException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
import ru.trader.core.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class MarketDocHandler extends DefaultHandler {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(MarketDocHandler.class);
|
||||
|
||||
protected final static String MARKET = "market";
|
||||
protected final static String ITEM_LIST = "items";
|
||||
protected final static String ITEM = "item";
|
||||
protected final static String VENDOR_LIST = "vendors";
|
||||
protected final static String VENDOR = "vendor";
|
||||
protected final static String OFFER = "offer";
|
||||
|
||||
protected final static String ID_ATTR = "id";
|
||||
protected final static String NAME_ATTR = "name";
|
||||
protected final static String TYPE_ATTR = "type";
|
||||
protected final static String PRICE_ATTR = "price";
|
||||
protected final static String ITEM_ATTR = "item";
|
||||
|
||||
protected Market world;
|
||||
protected Vendor curVendor;
|
||||
protected final HashMap<String,Item> items = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void startDocument() throws SAXException {
|
||||
world = new SimpleMarket();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
|
||||
switch (qName){
|
||||
case ITEM: parseItem(attributes);
|
||||
break;
|
||||
case VENDOR: parseVendor(attributes);
|
||||
break;
|
||||
case OFFER: parseOffer(attributes);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String qName) throws SAXException {
|
||||
switch (qName){
|
||||
case VENDOR: world.add(curVendor);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endDocument() throws SAXException {
|
||||
world.setChange(false);
|
||||
}
|
||||
|
||||
protected void parseVendor(Attributes attributes) throws SAXException {
|
||||
String name = attributes.getValue(NAME_ATTR);
|
||||
onVendor(name);
|
||||
}
|
||||
|
||||
protected void parseItem(Attributes attributes) throws SAXException {
|
||||
String name = attributes.getValue(NAME_ATTR);
|
||||
String id = attributes.getValue(ID_ATTR);
|
||||
onItem(name, id);
|
||||
}
|
||||
|
||||
protected void parseOffer(Attributes attributes) throws SAXException {
|
||||
String refid = attributes.getValue(ITEM_ATTR);
|
||||
Item item = items.get(refid);
|
||||
if (item == null)
|
||||
throw new SAXException(String.format("Item (id = %s) not found", refid));
|
||||
OFFER_TYPE offerType = OFFER_TYPE.valueOf(attributes.getValue(TYPE_ATTR));
|
||||
double price = Double.valueOf(attributes.getValue(PRICE_ATTR));
|
||||
onOffer(offerType, item, price);
|
||||
}
|
||||
|
||||
protected void onOffer(OFFER_TYPE offerType, Item item, double price){
|
||||
Offer offer = new Offer(offerType, item, price);
|
||||
curVendor.add(offer);
|
||||
}
|
||||
|
||||
protected void onVendor(String name){
|
||||
curVendor = new SimpleVendor(name);
|
||||
}
|
||||
|
||||
protected void onItem(String name, String id) {
|
||||
Item item = new Item(name);
|
||||
world.add(item);
|
||||
items.put(id, item);
|
||||
}
|
||||
|
||||
public Market getWorld(){
|
||||
return world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warning(SAXParseException e) throws SAXException {
|
||||
LOG.warn("warning on parse file",e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(SAXParseException e) throws SAXException {
|
||||
LOG.warn("Error on parse file",e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fatalError(SAXParseException e) throws SAXException {
|
||||
LOG.warn("Fatal error on parse file",e);
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
100
core/src/main/java/ru/trader/store/MarketStreamWriter.java
Normal file
100
core/src/main/java/ru/trader/store/MarketStreamWriter.java
Normal file
@@ -0,0 +1,100 @@
|
||||
package ru.trader.store;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import ru.trader.core.Item;
|
||||
import ru.trader.core.Market;
|
||||
import ru.trader.core.Offer;
|
||||
import ru.trader.core.Vendor;
|
||||
|
||||
import javax.xml.stream.XMLOutputFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.XMLStreamWriter;
|
||||
import java.io.*;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MarketStreamWriter {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(MarketStreamWriter.class);
|
||||
|
||||
private final Map<Item, String> items;
|
||||
private final Market market;
|
||||
private XMLStreamWriter out;
|
||||
|
||||
public MarketStreamWriter(Market market) {
|
||||
this.market = market;
|
||||
this.items = generateId(market.getItems());
|
||||
}
|
||||
|
||||
public void save(File xmlFile) throws XMLStreamException, UnsupportedEncodingException, FileNotFoundException {
|
||||
OutputStreamWriter outputStream = null;
|
||||
out = null;
|
||||
try {
|
||||
outputStream = new OutputStreamWriter(new FileOutputStream(xmlFile), "utf-8");
|
||||
out = XMLOutputFactory.newInstance().createXMLStreamWriter(outputStream);
|
||||
out.writeStartDocument();
|
||||
writeMarket();
|
||||
out.writeEndDocument();
|
||||
} finally {
|
||||
if (out!=null) try {out.close();} catch (XMLStreamException e) {LOG.warn("Error on close:",e);}
|
||||
if (outputStream!=null) try {outputStream.close();} catch (IOException e){LOG.warn("Error on close:",e);}
|
||||
}
|
||||
}
|
||||
|
||||
protected void writeMarket() throws XMLStreamException {
|
||||
out.writeStartElement(MarketDocHandler.MARKET);
|
||||
writeItems();
|
||||
writeVendors(market.get());
|
||||
out.writeEndElement();
|
||||
}
|
||||
|
||||
protected void writeItems() throws XMLStreamException {
|
||||
out.writeStartElement(MarketDocHandler.ITEM_LIST);
|
||||
for (Item entry : market.getItems()) {
|
||||
writeItem(entry, items.get(entry));
|
||||
}
|
||||
|
||||
out.writeEndElement();
|
||||
}
|
||||
|
||||
protected void writeItem(Item item, String id) throws XMLStreamException {
|
||||
out.writeEmptyElement(MarketDocHandler.ITEM);
|
||||
out.writeAttribute(MarketDocHandler.NAME_ATTR, item.getName());
|
||||
out.writeAttribute(MarketDocHandler.ID_ATTR, id);
|
||||
}
|
||||
|
||||
protected void writeVendors(Collection<Vendor> vendors) throws XMLStreamException {
|
||||
out.writeStartElement(MarketDocHandler.VENDOR_LIST);
|
||||
for (Vendor vendor : vendors) {
|
||||
writeVendor(vendor);
|
||||
}
|
||||
out.writeEndElement();
|
||||
}
|
||||
|
||||
protected void writeVendor(Vendor vendor) throws XMLStreamException {
|
||||
out.writeStartElement(MarketDocHandler.VENDOR);
|
||||
out.writeAttribute(MarketDocHandler.NAME_ATTR, vendor.getName());
|
||||
for (Offer offer : vendor.getAllOffers()) {
|
||||
writeOffer(offer);
|
||||
}
|
||||
out.writeEndElement();
|
||||
}
|
||||
|
||||
protected void writeOffer(Offer offer) throws XMLStreamException {
|
||||
out.writeEmptyElement(MarketDocHandler.OFFER);
|
||||
out.writeAttribute(MarketDocHandler.TYPE_ATTR, offer.getType().toString());
|
||||
out.writeAttribute(MarketDocHandler.ITEM_ATTR, items.get(offer.getItem()));
|
||||
out.writeAttribute(MarketDocHandler.PRICE_ATTR, String.valueOf(offer.getPrice()));
|
||||
}
|
||||
|
||||
private static Map<Item, String> generateId(Collection<Item> items){
|
||||
HashMap<Item, String> res = new HashMap<>(items.size());
|
||||
int index=0;
|
||||
for (Item item : items) {
|
||||
res.put(item, "i"+index);
|
||||
index++;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
67
core/src/main/java/ru/trader/store/Store.java
Normal file
67
core/src/main/java/ru/trader/store/Store.java
Normal file
@@ -0,0 +1,67 @@
|
||||
package ru.trader.store;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.xml.sax.SAXException;
|
||||
import ru.trader.core.Market;
|
||||
|
||||
import javax.xml.XMLConstants;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
import java.io.*;
|
||||
|
||||
public class Store {
|
||||
private final static Logger LOG = LoggerFactory.getLogger(Store.class);
|
||||
private final static String XSD_FILE = "/store/trader.xsd";
|
||||
private static Schema schema;
|
||||
|
||||
private static SAXParser getParser() throws SAXException, ParserConfigurationException {
|
||||
SAXParserFactory factory = SAXParserFactory.newInstance();
|
||||
factory.setValidating(false);
|
||||
factory.setNamespaceAware(true);
|
||||
if (schema == null) initSchema();
|
||||
factory.setSchema(schema);
|
||||
return factory.newSAXParser();
|
||||
}
|
||||
|
||||
public static Market loadFromFile(InputStream is) throws ParserConfigurationException, SAXException, IOException {
|
||||
return loadFromFile(is, new MarketDocHandler());
|
||||
}
|
||||
|
||||
public static Market loadFromFile(InputStream is, MarketDocHandler docHandler) throws ParserConfigurationException, SAXException, IOException {
|
||||
LOG.debug("Load market from stream");
|
||||
SAXParser parser = getParser();
|
||||
parser.parse(is, docHandler);
|
||||
return docHandler.getWorld();
|
||||
}
|
||||
|
||||
public static Market loadFromFile(File xmlFile) throws IOException, SAXException, ParserConfigurationException {
|
||||
return loadFromFile(xmlFile, new MarketDocHandler());
|
||||
}
|
||||
|
||||
public static Market loadFromFile(File xmlFile, MarketDocHandler docHandler) throws IOException, SAXException, ParserConfigurationException {
|
||||
LOG.debug("Load market from file {}", xmlFile);
|
||||
SAXParser parser = getParser();
|
||||
parser.parse(xmlFile, docHandler);
|
||||
return docHandler.getWorld();
|
||||
}
|
||||
|
||||
public static void saveToFile(Market market, File xmlFile) throws FileNotFoundException, UnsupportedEncodingException, XMLStreamException {
|
||||
LOG.debug("Save market {} to file {}", market, xmlFile);
|
||||
MarketStreamWriter writer = new MarketStreamWriter(market);
|
||||
writer.save(xmlFile);
|
||||
}
|
||||
|
||||
private static void initSchema() throws SAXException {
|
||||
Source xsdSource = new StreamSource(Store.class.getResourceAsStream(XSD_FILE));
|
||||
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
schema = schemaFactory.newSchema(xsdSource);
|
||||
}
|
||||
|
||||
}
|
||||
49
core/src/main/resources/store/trader.xsd
Normal file
49
core/src/main/resources/store/trader.xsd
Normal file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
|
||||
<xs:element name="market" type="marketType"/>
|
||||
|
||||
<xs:complexType name="marketType">
|
||||
<xs:sequence>
|
||||
<xs:element name="items" type="itemsType"/>
|
||||
<xs:element name="vendors" type="vendorsType"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="vendorsType">
|
||||
<xs:sequence>
|
||||
<xs:element name="vendor" type="vendorType" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="itemsType">
|
||||
<xs:sequence>
|
||||
<xs:element name="item" type="itemType" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="itemType">
|
||||
<xs:attribute name="id" type="xs:ID" use="required"/>
|
||||
<xs:attribute name="name" type="xs:string" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="vendorType" mixed="true">
|
||||
<xs:sequence>
|
||||
<xs:element name="offer" type="offerType" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
<xs:attribute name="name" type="xs:string" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:complexType name="offerType">
|
||||
<xs:attribute name="item" type="xs:IDREFS" use="required"/>
|
||||
<xs:attribute name="type" type="OFFER_TYPE" use="required"/>
|
||||
<xs:attribute name="price" type="xs:double" use="required"/>
|
||||
</xs:complexType>
|
||||
|
||||
<xs:simpleType name="OFFER_TYPE">
|
||||
<xs:restriction base="xs:string">
|
||||
<xs:enumeration value="SELL"/>
|
||||
<xs:enumeration value="BUY"/>
|
||||
</xs:restriction>
|
||||
</xs:simpleType>
|
||||
</xs:schema>
|
||||
102
core/src/test/java/ru/trader/TestUtil.java
Normal file
102
core/src/test/java/ru/trader/TestUtil.java
Normal file
@@ -0,0 +1,102 @@
|
||||
package ru.trader;
|
||||
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class TestUtil {
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> void assertCollectionEquals(Collection<T> collection, T... items){
|
||||
assertSize(collection, items);
|
||||
int curIndx=0;
|
||||
for (T actual : collection) {
|
||||
if (!actual.equals(items[curIndx])){
|
||||
Assert.fail(String.format("Entry by index %d is different. Expected: %s Actual: %s", curIndx, items[curIndx], actual));
|
||||
return;
|
||||
}
|
||||
curIndx++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static <T> void assertSize(Collection<T> collection, T[] items){
|
||||
int expectedSize = items.length;
|
||||
int actualSize = collection.size();
|
||||
if (actualSize!=expectedSize) {
|
||||
Assert.fail(String.format("Collection size differed. Expected: %d Actual: %d", expectedSize, actualSize));
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> void assertSize(Iterable<T> collection, T[] items){
|
||||
int expectedSize = items.length;
|
||||
Iterator<T> iterator = collection.iterator();
|
||||
int actualSize=0;
|
||||
while (iterator.hasNext()){actualSize++;iterator.next();}
|
||||
if (actualSize!=expectedSize) {
|
||||
Assert.fail(String.format("Collection size differed. Expected: %d Actual: %d", expectedSize, actualSize));
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> void checkContains(Collection<T> collection, boolean all, T[] items){
|
||||
if (all) assertSize(collection, items);
|
||||
for (T item : items) {
|
||||
if (!collection.contains(item)){
|
||||
Assert.fail(String.format("Collection should include an item %s", item));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static <T> boolean contains(Iterable<T> collection, T item){
|
||||
for (T t : collection) {
|
||||
if (item.equals(t)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static <T> void checkContains(Iterable<T> collection, boolean all, T[] items){
|
||||
if (all) assertSize(collection, items);
|
||||
for (T item : items) {
|
||||
if (!contains(collection, item)){
|
||||
Assert.fail(String.format("Collection should include an item %s", item));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> void assertCollectionNoContain(Collection<T> collection, T... items){
|
||||
for (T item : items) {
|
||||
if (collection.contains(item)){
|
||||
Assert.fail(String.format("Collection must not contain item %s", item));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> void assertCollectionContain(Collection<T> collection, T... items){
|
||||
checkContains(collection, false, items);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> void assertCollectionContainAll(Collection<T> collection, T... items){
|
||||
checkContains(collection, true, items);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> void assertIterableContain(Iterable<T> collection, T... items){
|
||||
checkContains(collection, false, items);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> void assertIterableContainAll(Iterable<T> collection, T... items){
|
||||
checkContains(collection, true, items);
|
||||
}
|
||||
|
||||
}
|
||||
43
core/src/test/java/ru/trader/core/ItemStatTest.java
Normal file
43
core/src/test/java/ru/trader/core/ItemStatTest.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package ru.trader.core;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
public class ItemStatTest extends Assert {
|
||||
private final static Item ITEM1 = new Item("Item1");
|
||||
|
||||
|
||||
private ItemStat itemSellStat = new ItemStat(ITEM1, OFFER_TYPE.SELL);
|
||||
private ItemStat itemBuyStat = new ItemStat(ITEM1, OFFER_TYPE.BUY);
|
||||
|
||||
@Before
|
||||
public void fill(){
|
||||
itemSellStat.put(new Offer(OFFER_TYPE.SELL, ITEM1, 10));
|
||||
itemSellStat.put(new Offer(OFFER_TYPE.SELL, ITEM1, 20));
|
||||
itemSellStat.put(new Offer(OFFER_TYPE.SELL, ITEM1, 30));
|
||||
itemSellStat.put(new Offer(OFFER_TYPE.SELL, ITEM1, 40));
|
||||
|
||||
itemBuyStat.put(new Offer(OFFER_TYPE.BUY, ITEM1, 100));
|
||||
itemBuyStat.put(new Offer(OFFER_TYPE.BUY, ITEM1, 200));
|
||||
itemBuyStat.put(new Offer(OFFER_TYPE.BUY, ITEM1, 300));
|
||||
itemBuyStat.put(new Offer(OFFER_TYPE.BUY, ITEM1, 400));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSell(){
|
||||
assertEquals(itemSellStat.getAvg(), (10+20+30+40)/4, 0);
|
||||
assertEquals(itemSellStat.getBest().getPrice(), 10d, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuy(){
|
||||
assertEquals(itemBuyStat.getAvg(), (100+200+300+400)/4, 0);
|
||||
assertEquals(itemBuyStat.getBest().getPrice(), 400d, 0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
84
core/src/test/java/ru/trader/core/MarketTest1.java
Normal file
84
core/src/test/java/ru/trader/core/MarketTest1.java
Normal file
@@ -0,0 +1,84 @@
|
||||
package ru.trader.core;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class MarketTest1 extends Assert {
|
||||
private final static Item ITEM1 = new Item("Item1");
|
||||
private final static Item ITEM2 = new Item("Item2");
|
||||
private final static Item ITEM3 = new Item("Item3");
|
||||
|
||||
private final static Offer bestSellOffer1 = new Offer(OFFER_TYPE.SELL,ITEM1,10);
|
||||
private final static Offer bestSellOffer2 = new Offer(OFFER_TYPE.SELL,ITEM2,15);
|
||||
private final static Offer bestSellOffer3 = new Offer(OFFER_TYPE.SELL,ITEM3,20);
|
||||
|
||||
private final static Offer bestBuyOffer1 = new Offer(OFFER_TYPE.BUY,ITEM1,100);
|
||||
private final static Offer bestBuyOffer2 = new Offer(OFFER_TYPE.BUY,ITEM2,200);
|
||||
private final static Offer bestBuyOffer3 = new Offer(OFFER_TYPE.BUY,ITEM3,100);
|
||||
private final static Offer bestBuyOffer4 = new Offer(OFFER_TYPE.BUY,ITEM2,150);
|
||||
|
||||
|
||||
private final static Vendor sellVendor1 = new SimpleVendor();
|
||||
private final static Vendor sellVendor2 = new SimpleVendor();
|
||||
private final static Vendor sellVendor3 = new SimpleVendor();
|
||||
private final static Vendor sellVendor4 = new SimpleVendor();
|
||||
private final static Vendor buyVendor1 = new SimpleVendor();
|
||||
private final static Vendor buyVendor2 = new SimpleVendor();
|
||||
private final static Vendor buyVendor3 = new SimpleVendor();
|
||||
private final static Vendor buyVendor4 = new SimpleVendor();
|
||||
|
||||
private Market market;
|
||||
|
||||
@Before
|
||||
public void fillMarket(){
|
||||
sellVendor1.add(bestSellOffer1);
|
||||
sellVendor1.add(new Offer(OFFER_TYPE.SELL,ITEM2,100));
|
||||
sellVendor2.add(new Offer(OFFER_TYPE.SELL,ITEM3,200));
|
||||
sellVendor2.add(bestSellOffer2);
|
||||
sellVendor3.add(new Offer(OFFER_TYPE.SELL,ITEM1,300));
|
||||
sellVendor3.add(new Offer(OFFER_TYPE.SELL,ITEM2,300));
|
||||
sellVendor3.add(bestSellOffer3);
|
||||
sellVendor4.add(new Offer(OFFER_TYPE.SELL,ITEM2,150));
|
||||
|
||||
buyVendor1.add(new Offer(OFFER_TYPE.BUY,ITEM2,50));
|
||||
buyVendor1.add(bestBuyOffer1);
|
||||
buyVendor2.add(new Offer(OFFER_TYPE.BUY,ITEM1,40));
|
||||
buyVendor2.add(bestBuyOffer2);
|
||||
buyVendor2.add(new Offer(OFFER_TYPE.BUY,ITEM3,50));
|
||||
buyVendor3.add(bestBuyOffer3);
|
||||
buyVendor3.add(new Offer(OFFER_TYPE.BUY,ITEM2,20));
|
||||
buyVendor4.add(new Offer(OFFER_TYPE.BUY,ITEM1,80));
|
||||
buyVendor4.add(bestBuyOffer4);
|
||||
|
||||
market = new SimpleMarket();
|
||||
market.add(sellVendor1);
|
||||
market.add(sellVendor2);
|
||||
market.add(sellVendor3);
|
||||
market.add(sellVendor4);
|
||||
market.add(buyVendor1);
|
||||
market.add(buyVendor2);
|
||||
market.add(buyVendor3);
|
||||
market.add(buyVendor4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBestSell(){
|
||||
Offer test = market.getStatSell(ITEM1).getBest();
|
||||
assertEquals(test, bestSellOffer1);
|
||||
test = market.getStatSell(ITEM2).getBest();
|
||||
assertEquals(test, bestSellOffer2);
|
||||
test = market.getStatSell(ITEM3).getBest();
|
||||
assertEquals(test, bestSellOffer3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBestBuy(){
|
||||
Offer test = market.getStatBuy(ITEM1).getBest();
|
||||
assertEquals(test, bestBuyOffer1);
|
||||
test = market.getStatBuy(ITEM2).getBest();
|
||||
assertEquals(test, bestBuyOffer2);
|
||||
test = market.getStatBuy(ITEM3).getBest();
|
||||
assertEquals(test, bestBuyOffer3);
|
||||
}
|
||||
}
|
||||
86
core/src/test/java/ru/trader/core/VendorTest.java
Normal file
86
core/src/test/java/ru/trader/core/VendorTest.java
Normal file
@@ -0,0 +1,86 @@
|
||||
package ru.trader.core;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
public class VendorTest extends Assert {
|
||||
private final static Item ITEM1 = new Item("Item1");
|
||||
private final static Item ITEM2 = new Item("Item2");
|
||||
private final static Offer SELL_OFFER = new Offer(OFFER_TYPE.SELL, ITEM1, 10);
|
||||
private final static Offer BUY_OFFER = new Offer(OFFER_TYPE.BUY, ITEM1, 10);
|
||||
|
||||
private Vendor sellVendor;
|
||||
private Vendor buyVendor;
|
||||
|
||||
@Before
|
||||
public void fillVendor(){
|
||||
sellVendor = new SimpleVendor();
|
||||
sellVendor.add(SELL_OFFER);
|
||||
buyVendor = new SimpleVendor();
|
||||
buyVendor.add(BUY_OFFER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddSellOffer(){
|
||||
final Iterable<Offer> offers = sellVendor.getAllSellOffers();
|
||||
Offer test = offers.iterator().next();
|
||||
assertEquals(test, SELL_OFFER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddSellOffer2(){
|
||||
final Iterable<Offer> offers = sellVendor.getAllBuyOffers();
|
||||
assertFalse(offers.iterator().hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddBuyOffer(){
|
||||
final Iterable<Offer> offers = buyVendor.getAllSellOffers();
|
||||
assertFalse(offers.iterator().hasNext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddBuyOffer2(){
|
||||
final Iterable<Offer> offers = buyVendor.getAllBuyOffers();
|
||||
Offer test = offers.iterator().next();
|
||||
assertEquals(test, BUY_OFFER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSellOfferOnSellVendor(){
|
||||
Offer test = sellVendor.getSell(ITEM1);
|
||||
assertEquals(test, SELL_OFFER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSellOfferOnBuyVendor(){
|
||||
Offer test = buyVendor.getSell(ITEM1);
|
||||
assertNull(test);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetWrongItemSellOfferOnSellVendor(){
|
||||
Offer test = buyVendor.getSell(ITEM2);
|
||||
assertNull(test);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBuyOfferOnSellVendor(){
|
||||
Offer test = sellVendor.getBuy(ITEM1);
|
||||
assertNull(test);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBuyOffersOnBuyVendor(){
|
||||
Offer test = buyVendor.getBuy(ITEM1);
|
||||
assertEquals(test, BUY_OFFER);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetWrongItemBuyOfferOnBuyVendor(){
|
||||
Offer test = sellVendor.getBuy(ITEM2);
|
||||
assertNull(test);
|
||||
}
|
||||
}
|
||||
66
core/src/test/java/ru/trader/core/VendorTest2.java
Normal file
66
core/src/test/java/ru/trader/core/VendorTest2.java
Normal file
@@ -0,0 +1,66 @@
|
||||
package ru.trader.core;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import ru.trader.TestUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class VendorTest2 extends Assert {
|
||||
private final static Item ITEM1 = new Item("Item1");
|
||||
private final static Item ITEM2 = new Item("Item2");
|
||||
private final static Item ITEM3 = new Item("Item3");
|
||||
private final static Offer SELL_OFFER1 = new Offer(OFFER_TYPE.SELL, ITEM1, 10);
|
||||
private final static Offer SELL_OFFER2 = new Offer(OFFER_TYPE.SELL, ITEM2, 10);
|
||||
private final static Offer SELL_OFFER3 = new Offer(OFFER_TYPE.SELL, ITEM3, 10);
|
||||
private final static Offer DUBLE_SELL_OFFER1 = new Offer(OFFER_TYPE.SELL, ITEM1, 100);
|
||||
|
||||
private final static Offer BUY_OFFER1 = new Offer(OFFER_TYPE.BUY, ITEM1, 100);
|
||||
private final static Offer BUY_OFFER2 = new Offer(OFFER_TYPE.BUY, ITEM2, 10);
|
||||
private final static Offer BUY_OFFER3 = new Offer(OFFER_TYPE.BUY, ITEM3, 10);
|
||||
private final static Offer DUBLE_BUY_OFFER1 = new Offer(OFFER_TYPE.BUY, ITEM1, 10);
|
||||
|
||||
|
||||
private Vendor sellVendor;
|
||||
private Vendor buyVendor;
|
||||
|
||||
@Before
|
||||
public void fillVendor(){
|
||||
sellVendor = new SimpleVendor();
|
||||
sellVendor.add(SELL_OFFER1);
|
||||
sellVendor.add(SELL_OFFER2);
|
||||
sellVendor.add(SELL_OFFER3);
|
||||
sellVendor.add(DUBLE_SELL_OFFER1);
|
||||
|
||||
buyVendor = new SimpleVendor();
|
||||
buyVendor.add(BUY_OFFER1);
|
||||
buyVendor.add(BUY_OFFER2);
|
||||
buyVendor.add(BUY_OFFER3);
|
||||
buyVendor.add(DUBLE_BUY_OFFER1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSellOffer(){
|
||||
final Offer test = sellVendor.getSell(ITEM1);
|
||||
assertEquals(test, DUBLE_SELL_OFFER1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBuyOffer(){
|
||||
final Offer test = buyVendor.getBuy(ITEM1);
|
||||
assertEquals(test, DUBLE_BUY_OFFER1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllSellOffer(){
|
||||
final Collection<Offer> test = sellVendor.getAllSellOffers();
|
||||
TestUtil.assertCollectionContainAll(test, DUBLE_SELL_OFFER1, SELL_OFFER2, SELL_OFFER3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAllBuyOffer(){
|
||||
final Collection<Offer> test = buyVendor.getAllBuyOffers();
|
||||
TestUtil.assertCollectionContainAll(test, DUBLE_BUY_OFFER1, BUY_OFFER3, BUY_OFFER2);
|
||||
}
|
||||
}
|
||||
26
core/src/test/java/ru/trader/store/LoadTest.java
Normal file
26
core/src/test/java/ru/trader/store/LoadTest.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package ru.trader.store;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.xml.sax.SAXException;
|
||||
import ru.trader.core.Market;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class LoadTest extends Assert {
|
||||
|
||||
@Test
|
||||
public void testLoad(){
|
||||
InputStream is = getClass().getResourceAsStream("/test.xml");
|
||||
Market world;
|
||||
try {
|
||||
world = Store.loadFromFile(is);
|
||||
} catch (ParserConfigurationException | SAXException | IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
assertNotNull(world);
|
||||
}
|
||||
|
||||
}
|
||||
16
core/src/test/resources/test.xml
Normal file
16
core/src/test/resources/test.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<market xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation = "../../main/resources/store/trader.xsd">
|
||||
<items>
|
||||
<item name="Item 1" id="i1"/>
|
||||
<item name="Item 2" id="i2"/>
|
||||
<item name="Item 3" id="i3"/>
|
||||
<item name="Item 4" id="i4"/>
|
||||
<item name="Item 5" id="i5"/>
|
||||
<item name="Item 6" id="i6"/>
|
||||
</items>
|
||||
<vendors>
|
||||
<vendor name="Vendor 1">
|
||||
<offer item="i1" type="SELL" price="140"/>
|
||||
</vendor>
|
||||
</vendors>
|
||||
</market>
|
||||
Reference in New Issue
Block a user