Create repository
This commit is contained in:
19
Trader.iml
Normal file
19
Trader.iml
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" 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" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.idea" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/client" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/core" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/elite" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/utils" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
|
|
||||||
BIN
client/ext-resources/all/1.xlsx
Normal file
BIN
client/ext-resources/all/1.xlsx
Normal file
Binary file not shown.
11
client/ext-resources/all/conf_/style.css
Normal file
11
client/ext-resources/all/conf_/style.css
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
.good .diff {
|
||||||
|
-fx-fill: green;
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bad .diff {
|
||||||
|
-fx-fill: blue;
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
1
client/ext-resources/all/world.xml
Normal file
1
client/ext-resources/all/world.xml
Normal file
File diff suppressed because one or more lines are too long
5
client/ext-resources/develop/log4j.properties
Normal file
5
client/ext-resources/develop/log4j.properties
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
log4j.rootLogger=DEBUG, stdout
|
||||||
|
|
||||||
|
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||||
|
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||||
|
log4j.appender.stdout.layout.ConversionPattern=%p: %d{dd.MM.yyyy HH:mm:ss} (%F:%L) - %m%n
|
||||||
12
client/ext-resources/release/log4j.properties
Normal file
12
client/ext-resources/release/log4j.properties
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
log4j.rootLogger=INFO, FILE
|
||||||
|
|
||||||
|
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||||
|
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||||
|
log4j.appender.stdout.layout.ConversionPattern=%p: %d{dd.MM.yyyy HH:mm:ss} (%F:%L) - %m%n
|
||||||
|
|
||||||
|
log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender
|
||||||
|
log4j.appender.FILE.File=traider.log
|
||||||
|
log4j.appender.FILE.DatePattern='.'yyyy-MM-dd'.log'
|
||||||
|
log4j.appender.FILE.threshold=INFO
|
||||||
|
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
|
||||||
|
log4j.appender.FILE.layout.ConversionPattern=%p: %d{dd.MM.yyyy HH:mm:ss} - %m%n
|
||||||
106
client/pom.xml
Normal file
106
client/pom.xml
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
<?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>client</artifactId>
|
||||||
|
|
||||||
|
<organization>
|
||||||
|
<name>Open Software</name>
|
||||||
|
</organization>
|
||||||
|
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>develop</id>
|
||||||
|
<activation>
|
||||||
|
<activeByDefault>true</activeByDefault>
|
||||||
|
</activation>
|
||||||
|
<properties>
|
||||||
|
<project.ext-resources>ext-resources/develop</project.ext-resources>
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
|
||||||
|
<profile>
|
||||||
|
<id>release</id>
|
||||||
|
<properties>
|
||||||
|
<project.ext-resources>ext-resources/release</project.ext-resources>
|
||||||
|
</properties>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>utils</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>log4j</groupId>
|
||||||
|
<artifactId>log4j</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-log4j12</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.controlsfx</groupId>
|
||||||
|
<artifactId>controlsfx</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.zenjava</groupId>
|
||||||
|
<artifactId>javafx-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<version>2.6</version>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-resources</id>
|
||||||
|
<phase>package</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-resources</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${project.build.directory}/resources</outputDirectory>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>ext-resources/all</directory>
|
||||||
|
<filtering>false</filtering>
|
||||||
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>${project.ext-resources}</directory>
|
||||||
|
<filtering>false</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
77
client/src/main/java/ru/trader/Main.java
Normal file
77
client/src/main/java/ru/trader/Main.java
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package ru.trader;
|
||||||
|
|
||||||
|
import javafx.application.Application;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import org.apache.log4j.PropertyConfigurator;
|
||||||
|
import org.controlsfx.control.action.Action;
|
||||||
|
import org.controlsfx.dialog.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import ru.trader.controllers.Screeners;
|
||||||
|
|
||||||
|
import javax.xml.stream.XMLStreamException;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
|
||||||
|
public class Main extends Application {
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(Main.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start(Stage primaryStage) throws Exception {
|
||||||
|
loadResources();
|
||||||
|
primaryStage.setTitle("Trader");
|
||||||
|
primaryStage.setScene(new Scene(Screeners.newScreeners(Main.class.getResource("/view/main.fxml"),getUrl("style.css").toExternalForm())));
|
||||||
|
primaryStage.setOnCloseRequest((we)->{
|
||||||
|
try {
|
||||||
|
if (World.getMarket().isChange()){
|
||||||
|
Action res = Screeners.showConfirm("Изменения не были сохранены, сохранить?");
|
||||||
|
if (res == Dialog.Actions.YES) World.save();
|
||||||
|
else if (res == Dialog.Actions.CANCEL) we.consume();
|
||||||
|
}
|
||||||
|
Screeners.closeAll();
|
||||||
|
} catch (FileNotFoundException | UnsupportedEncodingException | XMLStreamException e) {
|
||||||
|
LOG.error("Ошибка при сохранении",e);
|
||||||
|
Screeners.showException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
primaryStage.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop() throws Exception {
|
||||||
|
super.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
PropertyConfigurator.configure("log4j.properties");
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler((t, e) -> {
|
||||||
|
System.err.print("Exception in thread \"" + t.getName() + "\" ");
|
||||||
|
e.printStackTrace(System.err);
|
||||||
|
LOG.error("", e);
|
||||||
|
Screeners.showException(e);
|
||||||
|
});
|
||||||
|
launch(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static void loadResources() throws IOException {
|
||||||
|
Screeners.loadItemDescStage(getUrl(("itemDesc.fxml")));
|
||||||
|
Screeners.loadVEditorStage(getUrl(("vEditor.fxml")));
|
||||||
|
Screeners.loadAddOfferStage(getUrl(("oEditor.fxml")));
|
||||||
|
Screeners.loadOrdersStage(getUrl(("orders.fxml")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static URL getUrl(String filename) throws MalformedURLException {
|
||||||
|
File file = new File("conf"+File.separator+filename);
|
||||||
|
if (file.exists()) return file.toURI().toURL();
|
||||||
|
return Main.class.getResource("/view/"+filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
47
client/src/main/java/ru/trader/World.java
Normal file
47
client/src/main/java/ru/trader/World.java
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package ru.trader;
|
||||||
|
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import ru.trader.core.Market;
|
||||||
|
import ru.trader.core.SimpleMarket;
|
||||||
|
import ru.trader.model.ModelFabrica;
|
||||||
|
import ru.trader.store.Store;
|
||||||
|
import ru.trader.store.XSSFImporter;
|
||||||
|
|
||||||
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
import javax.xml.stream.XMLStreamException;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
public class World {
|
||||||
|
private static Market world;
|
||||||
|
private static final String STORE_FILE="world.xml";
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
File file = new File(STORE_FILE);
|
||||||
|
if (file.exists()) world = Store.loadFromFile(file);
|
||||||
|
else world = new SimpleMarket();
|
||||||
|
} catch (ParserConfigurationException | SAXException | IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void save() throws FileNotFoundException, UnsupportedEncodingException, XMLStreamException {
|
||||||
|
Store.saveToFile(world, new File("world.xml"));
|
||||||
|
world.setChange(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void imp(File file) throws IOException, SAXException {
|
||||||
|
XSSFImporter xssfImporter = new XSSFImporter(file);
|
||||||
|
world = xssfImporter.doImport();
|
||||||
|
ModelFabrica.clear();
|
||||||
|
world.setChange(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Market getMarket() {
|
||||||
|
return world;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package ru.trader.controllers;
|
||||||
|
|
||||||
|
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.control.ListView;
|
||||||
|
import javafx.util.Duration;
|
||||||
|
import org.controlsfx.control.PopOver;
|
||||||
|
import ru.trader.model.*;
|
||||||
|
|
||||||
|
|
||||||
|
public class ItemDescController {
|
||||||
|
|
||||||
|
|
||||||
|
private ItemDescModel item;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ListView<OfferModel> seller;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ListView<OfferModel> buyer;
|
||||||
|
|
||||||
|
public void setItemDesc(ItemDescModel itemDesc){
|
||||||
|
item = itemDesc;
|
||||||
|
fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fill(){
|
||||||
|
seller.setItems(FXCollections.observableList(item.getSeller()));
|
||||||
|
buyer.setItems(FXCollections.observableList(item.getBuyer()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private PopOver popup;
|
||||||
|
|
||||||
|
public void popup(Node owner, Parent itemDescScreen) {
|
||||||
|
if (popup != null && popup.isShowing()) return;
|
||||||
|
if (popup == null) {
|
||||||
|
popup = new PopOver(itemDescScreen);
|
||||||
|
popup.detach();
|
||||||
|
popup.setAutoHide(true);
|
||||||
|
|
||||||
|
}
|
||||||
|
popup.show(owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
if (popup!=null){
|
||||||
|
popup.hide(Duration.ZERO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
111
client/src/main/java/ru/trader/controllers/ItemsController.java
Normal file
111
client/src/main/java/ru/trader/controllers/ItemsController.java
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
package ru.trader.controllers;
|
||||||
|
|
||||||
|
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
|
import javafx.scene.input.MouseButton;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import ru.trader.model.*;
|
||||||
|
import ru.trader.model.support.ChangeMarketListener;
|
||||||
|
import ru.trader.model.support.ModelBindings;
|
||||||
|
|
||||||
|
|
||||||
|
public class ItemsController {
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(ItemsController.class);
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableView<ItemDescModel> tblItems;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableColumn<ItemDescModel, Number> minProfit;
|
||||||
|
@FXML
|
||||||
|
private TableColumn<ItemDescModel, Number> avgProfit;
|
||||||
|
@FXML
|
||||||
|
private TableColumn<ItemDescModel, Number> maxProfit;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void initialize() {
|
||||||
|
tblItems.getSelectionModel().selectedItemProperty().addListener((v, o, n) -> {
|
||||||
|
if (n!=null) Screeners.changeItemDesc(n);
|
||||||
|
});
|
||||||
|
tblItems.setOnMouseClicked((e) -> {
|
||||||
|
if (e.getButton() == MouseButton.SECONDARY){
|
||||||
|
Screeners.showItemDesc(tblItems);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
minProfit.setCellValueFactory((data) -> {
|
||||||
|
ItemDescModel iDesc = data.getValue();
|
||||||
|
return ModelBindings.diff(iDesc.minBuyProperty(),iDesc.maxSellProperty());
|
||||||
|
});
|
||||||
|
avgProfit.setCellValueFactory((data) -> {
|
||||||
|
ItemDescModel iDesc = data.getValue();
|
||||||
|
return iDesc.avgBuyProperty().subtract(iDesc.avgSellProperty());
|
||||||
|
});
|
||||||
|
maxProfit.setCellValueFactory((data) -> {
|
||||||
|
ItemDescModel iDesc = data.getValue();
|
||||||
|
return ModelBindings.diff(iDesc.maxBuyProperty(), iDesc.minSellProperty());
|
||||||
|
});
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(){
|
||||||
|
MarketModel market = MainController.getMarket();
|
||||||
|
market.addListener(new ItemsStatChangeListener());
|
||||||
|
tblItems.setItems(FXCollections.observableArrayList(market.itemsProperty()));
|
||||||
|
if (tblItems.getSortOrder().size()>0)
|
||||||
|
tblItems.sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refresh(OfferModel offer){
|
||||||
|
LOG.info("Refresh item desc link with item of offer {}", offer);
|
||||||
|
for (ItemDescModel descModel : tblItems.getItems()) {
|
||||||
|
if (descModel.hasItem(offer)){
|
||||||
|
descModel.refresh(offer.getType());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refresh(){
|
||||||
|
LOG.info("Refresh all stats");
|
||||||
|
tblItems.getItems().forEach(ItemDescModel::refresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addItem(ItemDescModel item){
|
||||||
|
tblItems.getItems().add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ItemsStatChangeListener extends ChangeMarketListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(ItemDescModel item) {
|
||||||
|
addItem(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(OfferModel offer) {
|
||||||
|
refresh(offer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(VendorModel vendor) {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(OfferModel offer) {
|
||||||
|
refresh(offer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void priceChange(OfferModel offer, double price, double value) {
|
||||||
|
refresh(offer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
106
client/src/main/java/ru/trader/controllers/MainController.java
Normal file
106
client/src/main/java/ru/trader/controllers/MainController.java
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
package ru.trader.controllers;
|
||||||
|
|
||||||
|
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.layout.BorderPane;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
|
import org.controlsfx.dialog.Dialogs;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import ru.trader.World;
|
||||||
|
import ru.trader.model.MarketModel;
|
||||||
|
|
||||||
|
import javax.xml.stream.XMLStreamException;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class MainController {
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(MainController.class);
|
||||||
|
|
||||||
|
private static MarketModel world = new MarketModel(World.getMarket());
|
||||||
|
private static MarketModel market = new MarketModel(World.getMarket());
|
||||||
|
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private BorderPane mainPane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private OffersController offersController;
|
||||||
|
@FXML
|
||||||
|
private ItemsController itemsController;
|
||||||
|
|
||||||
|
public OffersController getOffersController() {
|
||||||
|
return offersController;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BorderPane getMainPane(){
|
||||||
|
return mainPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MarketModel getMarket() {
|
||||||
|
return market;
|
||||||
|
}
|
||||||
|
public static MarketModel getWorld() {
|
||||||
|
return world;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMarket(MarketModel market) {
|
||||||
|
MarketModel old = MainController.market;
|
||||||
|
MainController.market = market;
|
||||||
|
MainController.market.addAllListener(old.getListeners());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save(ActionEvent actionEvent) {
|
||||||
|
try {
|
||||||
|
World.save();
|
||||||
|
} catch (FileNotFoundException | UnsupportedEncodingException | XMLStreamException e) {
|
||||||
|
LOG.error("Error on save file",e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void importWorld(ActionEvent actionEvent) {
|
||||||
|
try {
|
||||||
|
FileChooser fileChooser = new FileChooser();
|
||||||
|
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("Excel files (*.xlsx)", "*.xlsx");
|
||||||
|
fileChooser.getExtensionFilters().add(extFilter);
|
||||||
|
fileChooser.setInitialDirectory(new File("."));
|
||||||
|
File file = fileChooser.showOpenDialog(null);
|
||||||
|
if (file !=null) {
|
||||||
|
World.imp(file);
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
} catch (SAXException | IOException e) {
|
||||||
|
LOG.error("Error on import file", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void addItem(ActionEvent actionEvent){
|
||||||
|
Optional<String> res = Dialogs.create()
|
||||||
|
.title("Добавление нового товара")
|
||||||
|
.message("Введите название товара")
|
||||||
|
.showTextInput();
|
||||||
|
if (res.isPresent()) market.add(market.newItem(res.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void addVendor(ActionEvent actionEvent) {
|
||||||
|
Screeners.showAddVendor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void editVendor(ActionEvent actionEvent) {
|
||||||
|
Screeners.showEditVendor(offersController.getVendor());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reload(){
|
||||||
|
world = new MarketModel(World.getMarket());
|
||||||
|
market = world;
|
||||||
|
itemsController.init();
|
||||||
|
offersController.init();
|
||||||
|
}
|
||||||
|
}
|
||||||
163
client/src/main/java/ru/trader/controllers/OffersController.java
Normal file
163
client/src/main/java/ru/trader/controllers/OffersController.java
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
package ru.trader.controllers;
|
||||||
|
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.ComboBox;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
|
import ru.trader.core.Vendor;
|
||||||
|
import ru.trader.model.*;
|
||||||
|
import ru.trader.model.support.ChangeMarketListener;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
|
||||||
|
public class OffersController {
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(OffersController.class);
|
||||||
|
|
||||||
|
private VendorModel vendor;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ComboBox<Vendor> vendors;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableView<OfferDescModel> tblSell;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableView<OfferDescModel> tblBuy;
|
||||||
|
|
||||||
|
// инициализируем форму данными
|
||||||
|
@FXML
|
||||||
|
private void initialize() {
|
||||||
|
vendors.getSelectionModel().selectedItemProperty().addListener((ob, oldValue, newValue) ->{
|
||||||
|
if (newValue != null){
|
||||||
|
LOG.info("Change vendor to {}", newValue);
|
||||||
|
setVendor(newValue);
|
||||||
|
fillTables(vendor);
|
||||||
|
} else {
|
||||||
|
vendors.getSelectionModel().select(oldValue);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(){
|
||||||
|
MarketModel market = MainController.getMarket();
|
||||||
|
market.addListener(new OffersChangeListener());
|
||||||
|
vendors.setItems(market.vendorsProperty());
|
||||||
|
vendors.getSelectionModel().selectFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillTables(VendorModel vendor){
|
||||||
|
if (vendor != null){
|
||||||
|
tblSell.setItems(FXCollections.observableList(vendor.getSells(this::asOfferDescModel)));
|
||||||
|
if (tblSell.getSortOrder().size()>0)
|
||||||
|
tblSell.sort();
|
||||||
|
|
||||||
|
tblBuy.setItems(FXCollections.observableList(vendor.getBuys(this::asOfferDescModel)));
|
||||||
|
if (tblBuy.getSortOrder().size()>0)
|
||||||
|
tblBuy.sort();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
tblSell.getItems().clear();
|
||||||
|
tblBuy.getItems().clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
public void editPrice(TableColumn.CellEditEvent<OfferDescModel, Double> event){
|
||||||
|
OfferModel offer = event.getRowValue().getOffer();
|
||||||
|
offer.setPrice(event.getNewValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public VendorModel getVendor() {
|
||||||
|
return vendor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setVendor(Vendor vendor){
|
||||||
|
this.vendor = MainController.getMarket().asModel(vendor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OfferDescModel asOfferDescModel(OfferModel offer){
|
||||||
|
return MainController.getMarket().asOfferDescModel(offer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addOffer(OfferModel offer){
|
||||||
|
switch (offer.getType()){
|
||||||
|
case SELL: tblSell.getItems().add(asOfferDescModel(offer));
|
||||||
|
break;
|
||||||
|
case BUY: tblBuy.getItems().add(asOfferDescModel(offer));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeOffer(OfferModel offer){
|
||||||
|
switch (offer.getType()){
|
||||||
|
case SELL: remove(offer, tblSell.getItems());
|
||||||
|
break;
|
||||||
|
case BUY: remove(offer, tblBuy.getItems());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void remove(final OfferModel offer, final Collection<OfferDescModel> list){
|
||||||
|
Iterator<OfferDescModel> iterator = list.iterator();
|
||||||
|
while (iterator.hasNext()){
|
||||||
|
if (iterator.next().getOffer().equals(offer)){
|
||||||
|
iterator.remove();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refresh(OfferModel offer){
|
||||||
|
LOG.info("Refresh lists link with item of offer {}", offer);
|
||||||
|
for (OfferDescModel descModel : tblSell.getItems()) {
|
||||||
|
if (descModel.hasItem(offer)){
|
||||||
|
descModel.refresh(offer.getType());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (OfferDescModel descModel : tblBuy.getItems()) {
|
||||||
|
if (descModel.hasItem(offer)){
|
||||||
|
descModel.refresh(offer.getType());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refresh(){
|
||||||
|
LOG.info("Refresh lists");
|
||||||
|
tblSell.getItems().forEach(OfferDescModel::refresh);
|
||||||
|
tblBuy.getItems().forEach(OfferDescModel::refresh);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class OffersChangeListener extends ChangeMarketListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(OfferModel offer) {
|
||||||
|
refresh(offer);
|
||||||
|
if (offer.hasVendor(vendor)){
|
||||||
|
addOffer(offer);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(VendorModel vendor) {
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(OfferModel offer) {
|
||||||
|
refresh(offer);
|
||||||
|
if (offer.hasVendor(vendor)) {
|
||||||
|
removeOffer(offer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package ru.trader.controllers;
|
||||||
|
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import org.controlsfx.control.ButtonBar;
|
||||||
|
import org.controlsfx.control.action.AbstractAction;
|
||||||
|
import org.controlsfx.control.action.Action;
|
||||||
|
import org.controlsfx.dialog.Dialog;
|
||||||
|
import ru.trader.model.ItemModel;
|
||||||
|
import ru.trader.view.support.NumberField;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class OffersEditorController {
|
||||||
|
private final Action OK = new AbstractAction("OK") {
|
||||||
|
{
|
||||||
|
ButtonBar.setType(this, ButtonBar.ButtonType.OK_DONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(ActionEvent event) {
|
||||||
|
Dialog dlg = (Dialog) event.getSource();
|
||||||
|
dlg.hide();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Label name;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private NumberField sell;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private NumberField buy;
|
||||||
|
|
||||||
|
|
||||||
|
public Optional<DialogResult> showDialog(Parent parent, Parent content, ItemModel item, Number sell, Number buy) {
|
||||||
|
name.setText(item.getName());
|
||||||
|
|
||||||
|
this.sell.setValue(sell);
|
||||||
|
this.buy.setValue(buy);
|
||||||
|
|
||||||
|
OK.disabledProperty().bind(this.sell.wrongProperty().or(this.buy.wrongProperty()));
|
||||||
|
|
||||||
|
Dialog dlg = new Dialog(parent, "Создание заказов");
|
||||||
|
dlg.setContent(content);
|
||||||
|
dlg.getActions().addAll(OK, Dialog.Actions.CANCEL);
|
||||||
|
dlg.setResizable(false);
|
||||||
|
return Optional.ofNullable(dlg.show() == OK ? new DialogResult() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public class DialogResult {
|
||||||
|
|
||||||
|
private double _sell;
|
||||||
|
private double _buy;
|
||||||
|
|
||||||
|
public DialogResult() {
|
||||||
|
_sell = sell.getValue().doubleValue();
|
||||||
|
_buy = buy.getValue().doubleValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getSell() {
|
||||||
|
return _sell;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getBuy() {
|
||||||
|
return _buy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
102
client/src/main/java/ru/trader/controllers/OrdersController.java
Normal file
102
client/src/main/java/ru/trader/controllers/OrdersController.java
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package ru.trader.controllers;
|
||||||
|
|
||||||
|
import javafx.beans.property.SimpleDoubleProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
|
import javafx.scene.control.cell.TextFieldTableCell;
|
||||||
|
import javafx.util.converter.LongStringConverter;
|
||||||
|
import org.controlsfx.control.ButtonBar;
|
||||||
|
import org.controlsfx.control.action.AbstractAction;
|
||||||
|
import org.controlsfx.control.action.Action;
|
||||||
|
import org.controlsfx.dialog.Dialog;
|
||||||
|
import ru.trader.model.OfferDescModel;
|
||||||
|
import ru.trader.model.OfferModel;
|
||||||
|
import ru.trader.model.OrderModel;
|
||||||
|
import ru.trader.model.support.BindingsHelper;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class OrdersController {
|
||||||
|
private final Action OK = new AbstractAction("OK") {
|
||||||
|
{
|
||||||
|
ButtonBar.setType(this, ButtonBar.ButtonType.OK_DONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(ActionEvent event) {
|
||||||
|
Dialog dlg = (Dialog) event.getSource();
|
||||||
|
dlg.hide();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableView<OrderModel> tblOrders;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableView<OfferModel> tblBuyers;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableColumn<OrderModel, Long> count;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableColumn<OrderModel, Long> maxCount;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableColumn<OfferModel, Double> curProfit;
|
||||||
|
|
||||||
|
private OrderModel order;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void initialize() {
|
||||||
|
count.setCellFactory(TextFieldTableCell.forTableColumn(new LongStringConverter()));
|
||||||
|
maxCount.setCellFactory(TextFieldTableCell.forTableColumn(new LongStringConverter()));
|
||||||
|
tblOrders.getSelectionModel().selectedItemProperty().addListener((v, o, n) -> changeOrder(n));
|
||||||
|
tblBuyers.getSelectionModel().selectedItemProperty().addListener((v, o, n) -> setBuyer(n));
|
||||||
|
curProfit.setCellValueFactory(param -> {
|
||||||
|
OfferModel offer = param.getValue();
|
||||||
|
return order !=null ? order.getProfit(offer) : new SimpleDoubleProperty(Double.NaN).asObject();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Optional<OrderModel> showDialog(Parent parent, Parent content, Collection<OfferDescModel> offers, double balance, long max) {
|
||||||
|
|
||||||
|
init(offers, balance, max);
|
||||||
|
|
||||||
|
Dialog dlg = new Dialog(parent, "Создание заказов");
|
||||||
|
dlg.setContent(content);
|
||||||
|
dlg.getActions().addAll(OK, Dialog.Actions.CANCEL);
|
||||||
|
dlg.setResizable(false);
|
||||||
|
return Optional.ofNullable(dlg.show() == OK ? tblOrders.getSelectionModel().getSelectedItem() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init(Collection<OfferDescModel> offers, double balance, long max) {
|
||||||
|
tblOrders.setItems(BindingsHelper.observableList(offers, (o) -> new OrderModel(o, balance, max)));
|
||||||
|
if (tblOrders.getSortOrder().size()>0)
|
||||||
|
tblOrders.sort();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void changeOrder(OrderModel order) {
|
||||||
|
this.order = order;
|
||||||
|
if (order != null) tblBuyers.setItems(FXCollections.observableList(order.getBuyers()));
|
||||||
|
else tblBuyers.setItems(FXCollections.emptyObservableList());
|
||||||
|
tblBuyers.getSelectionModel().clearSelection();
|
||||||
|
if (tblBuyers.getSortOrder().size()>0)
|
||||||
|
tblBuyers.sort();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setBuyer(OfferModel offer) {
|
||||||
|
if (order != null) {
|
||||||
|
order.setBuyer(offer);
|
||||||
|
order.setCount(order.getMax());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package ru.trader.controllers;
|
||||||
|
|
||||||
|
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.ComboBox;
|
||||||
|
import ru.trader.core.Vendor;
|
||||||
|
import ru.trader.model.MarketModel;
|
||||||
|
import ru.trader.model.OfferDescModel;
|
||||||
|
import ru.trader.view.support.NumberField;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
|
||||||
|
public class RoutersController {
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private NumberField balance;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private NumberField cargo;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button buy;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private Button sell;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private ComboBox<Vendor> vendors;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void initialize(){
|
||||||
|
init();
|
||||||
|
buy.disableProperty().bind(this.balance.wrongProperty().or(this.cargo.wrongProperty()));
|
||||||
|
buy.setOnAction((e) -> Screeners.showOrders(getOffers(), balance.getValue().doubleValue(), cargo.getValue().longValue()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(){
|
||||||
|
MarketModel market = MainController.getMarket();
|
||||||
|
vendors.setItems(market.vendorsProperty());
|
||||||
|
vendors.getSelectionModel().selectFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Collection<OfferDescModel> getOffers(){
|
||||||
|
MarketModel market = MainController.getMarket();
|
||||||
|
Vendor vendor = vendors.getSelectionModel().getSelectedItem();
|
||||||
|
return vendor.getAllSellOffers().stream().map(market::asOfferDescModel).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
}
|
||||||
117
client/src/main/java/ru/trader/controllers/Screeners.java
Normal file
117
client/src/main/java/ru/trader/controllers/Screeners.java
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
package ru.trader.controllers;
|
||||||
|
|
||||||
|
import javafx.fxml.FXMLLoader;
|
||||||
|
import javafx.scene.Node;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.Scene;
|
||||||
|
import javafx.stage.Stage;
|
||||||
|
import org.controlsfx.control.action.Action;
|
||||||
|
import org.controlsfx.dialog.Dialogs;
|
||||||
|
import ru.trader.model.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class Screeners {
|
||||||
|
|
||||||
|
private static Parent mainScreen;
|
||||||
|
private static Parent itemDescScreen;
|
||||||
|
private static Parent vEditorScreen;
|
||||||
|
private static Parent editOffersScreen;
|
||||||
|
private static Parent ordersScreen;
|
||||||
|
|
||||||
|
private static MainController mainController;
|
||||||
|
private static ItemDescController itemDescController;
|
||||||
|
private static VendorEditorController vEditorController;
|
||||||
|
private static OffersEditorController oEditorController;
|
||||||
|
private static OrdersController ordersController;
|
||||||
|
|
||||||
|
public static Parent newScreeners(URL main, String stylesheet) throws IOException {
|
||||||
|
FXMLLoader loader = new FXMLLoader(main);
|
||||||
|
mainScreen = loader.load();
|
||||||
|
if (stylesheet!=null)
|
||||||
|
mainScreen.getStylesheets().add(stylesheet);
|
||||||
|
mainController = loader.getController();
|
||||||
|
return mainScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void loadItemDescStage(URL fxml) throws IOException {
|
||||||
|
FXMLLoader loader = new FXMLLoader(fxml);
|
||||||
|
itemDescScreen = loader.load();
|
||||||
|
itemDescController = loader.getController();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadVEditorStage(URL fxml) throws IOException {
|
||||||
|
FXMLLoader loader = new FXMLLoader(fxml);
|
||||||
|
vEditorScreen = loader.load();
|
||||||
|
vEditorController = loader.getController();
|
||||||
|
Stage stage = new Stage();
|
||||||
|
stage.setScene(new Scene(vEditorScreen));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadAddOfferStage(URL fxml) throws IOException {
|
||||||
|
FXMLLoader loader = new FXMLLoader(fxml);
|
||||||
|
editOffersScreen = loader.load();
|
||||||
|
oEditorController = loader.getController();
|
||||||
|
Stage stage = new Stage();
|
||||||
|
stage.setScene(new Scene(editOffersScreen));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadOrdersStage(URL fxml) throws IOException {
|
||||||
|
FXMLLoader loader = new FXMLLoader(fxml);
|
||||||
|
ordersScreen = loader.load();
|
||||||
|
ordersController = loader.getController();
|
||||||
|
Stage stage = new Stage();
|
||||||
|
stage.setScene(new Scene(ordersScreen));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void show(Node node){
|
||||||
|
mainController.getMainPane().setCenter(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showException(Throwable e){
|
||||||
|
if (mainScreen!=null)
|
||||||
|
Dialogs.create().owner(mainScreen).showException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Action showConfirm(String text){
|
||||||
|
return Dialogs.create().owner(mainScreen).message(text).showConfirm();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Action showAddVendor(){
|
||||||
|
return vEditorController.showDialog(mainScreen, vEditorScreen, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Action showEditVendor(VendorModel vendor){
|
||||||
|
return vEditorController.showDialog(mainScreen, vEditorScreen, vendor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Parent getMainScreen(){
|
||||||
|
return mainScreen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Optional<OffersEditorController.DialogResult> showEditOffers(ItemModel item, Number sell, Number buy) {
|
||||||
|
return oEditorController.showDialog(vEditorScreen, editOffersScreen, item, sell, buy);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Optional<OrderModel> showOrders(Collection<OfferDescModel> offers, double balance, long cargo) {
|
||||||
|
return ordersController.showDialog(mainScreen, ordersScreen, offers, balance, cargo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void changeItemDesc(ItemDescModel item){
|
||||||
|
itemDescController.setItemDesc(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void showItemDesc(Node owner){
|
||||||
|
itemDescController.popup(owner, itemDescScreen);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void closeAll() {
|
||||||
|
itemDescController.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
package ru.trader.controllers;
|
||||||
|
|
||||||
|
import javafx.beans.property.DoubleProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyStringProperty;
|
||||||
|
import javafx.beans.property.SimpleDoubleProperty;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
|
import javafx.fxml.FXML;
|
||||||
|
import javafx.scene.Parent;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.scene.control.TableView;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.util.converter.DoubleStringConverter;
|
||||||
|
import org.controlsfx.control.ButtonBar;
|
||||||
|
import org.controlsfx.control.action.AbstractAction;
|
||||||
|
import org.controlsfx.control.action.Action;
|
||||||
|
import org.controlsfx.dialog.Dialog;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import ru.trader.core.OFFER_TYPE;
|
||||||
|
import ru.trader.model.*;
|
||||||
|
import ru.trader.model.support.BindingsHelper;
|
||||||
|
import ru.trader.view.support.cells.TextFieldCell;
|
||||||
|
|
||||||
|
|
||||||
|
public class VendorEditorController {
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(VendorEditorController.class);
|
||||||
|
|
||||||
|
private VendorModel vendor;
|
||||||
|
|
||||||
|
private final Action actSave = new AbstractAction("Сохранить") {
|
||||||
|
{
|
||||||
|
ButtonBar.setType(this, ButtonBar.ButtonType.OK_DONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(ActionEvent event) {
|
||||||
|
Dialog dlg = (Dialog) event.getSource();
|
||||||
|
saveChanges();
|
||||||
|
dlg.hide();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TextField name;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private TableView<FakeOffer> items;
|
||||||
|
@FXML
|
||||||
|
private TableColumn<FakeOffer, Double> buy;
|
||||||
|
@FXML
|
||||||
|
private TableColumn<FakeOffer, Double> sell;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private void initialize() {
|
||||||
|
items.getSelectionModel().setCellSelectionEnabled(true);
|
||||||
|
buy.setCellFactory(TextFieldCell.forTableColumn(new DoubleStringConverter()));
|
||||||
|
sell.setCellFactory(TextFieldCell.forTableColumn(new DoubleStringConverter()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action showDialog(Parent parent, Parent content, VendorModel vendor){
|
||||||
|
this.vendor = vendor;
|
||||||
|
if (vendor != null) {
|
||||||
|
fill();
|
||||||
|
} else {
|
||||||
|
reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialog dlg = new Dialog(parent, vendor == null ? "Добавление станции" : "Редактирование станции");
|
||||||
|
dlg.setContent(content);
|
||||||
|
dlg.getActions().addAll(actSave, Dialog.Actions.CANCEL);
|
||||||
|
dlg.setResizable(false);
|
||||||
|
return dlg.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void fill(){
|
||||||
|
name.setText(vendor.getName());
|
||||||
|
fillItems();
|
||||||
|
vendor.getSells().forEach(this::fillOffer);
|
||||||
|
vendor.getBuys().forEach(this::fillOffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void reset(){
|
||||||
|
name.setText("");
|
||||||
|
fillItems();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillItems() {
|
||||||
|
items.setItems(BindingsHelper.observableList(MainController.getMarket().itemsProperty(), (item) -> new FakeOffer(item.getItem())));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void fillOffer(OfferModel offer) {
|
||||||
|
for (FakeOffer o : items.getItems()) {
|
||||||
|
if (offer.hasItem(o.item)) {
|
||||||
|
switch (offer.getType()) {
|
||||||
|
case SELL:
|
||||||
|
o.setSell(offer);
|
||||||
|
break;
|
||||||
|
case BUY:
|
||||||
|
o.setBuy(offer);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveChanges(){
|
||||||
|
LOG.info("Save vendor changes");
|
||||||
|
items.getSelectionModel().clearSelection();
|
||||||
|
final MarketModel market = MainController.getMarket();
|
||||||
|
if (vendor == null) {
|
||||||
|
market.setAlert(false);
|
||||||
|
vendor = market.newVendor(name.getText());
|
||||||
|
items.getItems().forEach((o) -> commit(market, vendor, o));
|
||||||
|
market.setAlert(true);
|
||||||
|
market.add(vendor);
|
||||||
|
} else {
|
||||||
|
vendor.setName(name.getText());
|
||||||
|
items.getItems().forEach((o) -> commit(market, vendor, o));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void commit(MarketModel market, VendorModel vendor, FakeOffer offer){
|
||||||
|
LOG.trace("Commit changes of offers {}", offer);
|
||||||
|
if (offer.isBlank()){
|
||||||
|
LOG.trace("Is blank offer, skip");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offer.isNewBuy()){
|
||||||
|
LOG.trace("Is new buy offer");
|
||||||
|
vendor.add(market.newOffer(OFFER_TYPE.BUY, offer.item, offer.getBprice()));
|
||||||
|
} else if (offer.isRemoveBuy()) {
|
||||||
|
LOG.trace("Is remove buy offer");
|
||||||
|
vendor.remove(offer.buy);
|
||||||
|
} else if (offer.isChangeBuy()){
|
||||||
|
LOG.trace("Is change buy price to {}", offer.getBprice());
|
||||||
|
offer.buy.setPrice(offer.getBprice());
|
||||||
|
} else {
|
||||||
|
LOG.trace("No change buy offer");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (offer.isNewSell()){
|
||||||
|
LOG.trace("Is new sell offer");
|
||||||
|
vendor.add(market.newOffer(OFFER_TYPE.SELL, offer.item, offer.getSprice()));
|
||||||
|
} else if (offer.isRemoveSell()) {
|
||||||
|
LOG.trace("Is remove sell offer");
|
||||||
|
vendor.remove(offer.sell);
|
||||||
|
} else if (offer.isChangeSell()){
|
||||||
|
LOG.trace("Is change sell price to {}", offer.getSprice());
|
||||||
|
offer.sell.setPrice(offer.getSprice());
|
||||||
|
} else {
|
||||||
|
LOG.trace("No change sell offer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FakeOffer {
|
||||||
|
private final ItemModel item;
|
||||||
|
private DoubleProperty sprice;
|
||||||
|
private DoubleProperty bprice;
|
||||||
|
private OfferModel sell;
|
||||||
|
private OfferModel buy;
|
||||||
|
|
||||||
|
public FakeOffer(ItemModel item){
|
||||||
|
this.item = item;
|
||||||
|
this.sprice = new SimpleDoubleProperty(0);
|
||||||
|
this.bprice = new SimpleDoubleProperty(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyStringProperty nameProperty(){
|
||||||
|
return item.nameProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getSprice() {
|
||||||
|
return sprice.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSprice(double sprice) {
|
||||||
|
this.sprice.set(sprice);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getBprice() {
|
||||||
|
return bprice.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBprice(double bprice) {
|
||||||
|
this.bprice.set(bprice);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DoubleProperty bpriceProperty() {
|
||||||
|
return bprice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DoubleProperty spriceProperty() {
|
||||||
|
return sprice;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isChangeSell() {
|
||||||
|
return sell!=null && getSprice() != sell.getPrice();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isChangeBuy() {
|
||||||
|
return buy!=null && getBprice() != buy.getPrice();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public boolean isNewSell() {
|
||||||
|
return sell == null && getSprice() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isNewBuy() {
|
||||||
|
return buy == null && getBprice() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRemoveSell() {
|
||||||
|
return sell != null && getSprice() ==0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRemoveBuy() {
|
||||||
|
return buy != null && getBprice() ==0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBlank(){
|
||||||
|
return sell == null && getSprice() == 0 && buy == null && getBprice() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasItem(ItemModel item){
|
||||||
|
return this.item.equals(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSell(OfferModel sell) {
|
||||||
|
this.sell = sell;
|
||||||
|
sprice.set(sell.getPrice());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBuy(OfferModel buy) {
|
||||||
|
this.buy = buy;
|
||||||
|
bprice.set(buy.getPrice());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "FakeOffer{" +
|
||||||
|
"item=" + item +
|
||||||
|
", sprice=" + sprice.get() +
|
||||||
|
", bprice=" + bprice.get() +
|
||||||
|
", sell=" + sell +
|
||||||
|
", buy=" + buy +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
40
client/src/main/java/ru/trader/model/ItemDescModel.java
Normal file
40
client/src/main/java/ru/trader/model/ItemDescModel.java
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package ru.trader.model;
|
||||||
|
|
||||||
|
import javafx.beans.property.*;
|
||||||
|
import ru.trader.core.OFFER_TYPE;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
public interface ItemDescModel {
|
||||||
|
ItemModel getItem();
|
||||||
|
|
||||||
|
ReadOnlyStringProperty nameProperty();
|
||||||
|
|
||||||
|
ReadOnlyDoubleProperty avgBuyProperty();
|
||||||
|
|
||||||
|
ReadOnlyObjectProperty<OfferModel> minBuyProperty();
|
||||||
|
|
||||||
|
ReadOnlyObjectProperty<OfferModel> maxBuyProperty();
|
||||||
|
|
||||||
|
ReadOnlyObjectProperty<OfferModel> bestBuyProperty();
|
||||||
|
|
||||||
|
ReadOnlyDoubleProperty avgSellProperty();
|
||||||
|
|
||||||
|
ReadOnlyObjectProperty<OfferModel> minSellProperty();
|
||||||
|
|
||||||
|
ReadOnlyObjectProperty<OfferModel> maxSellProperty();
|
||||||
|
|
||||||
|
ReadOnlyObjectProperty<OfferModel> bestSellProperty();
|
||||||
|
|
||||||
|
|
||||||
|
boolean hasItem(OfferModel offer);
|
||||||
|
|
||||||
|
List<OfferModel> getSeller();
|
||||||
|
|
||||||
|
List<OfferModel> getBuyer();
|
||||||
|
|
||||||
|
void refresh();
|
||||||
|
|
||||||
|
void refresh(OFFER_TYPE type);
|
||||||
|
}
|
||||||
115
client/src/main/java/ru/trader/model/ItemDescModelImpl.java
Normal file
115
client/src/main/java/ru/trader/model/ItemDescModelImpl.java
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
package ru.trader.model;
|
||||||
|
|
||||||
|
import javafx.beans.property.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import ru.trader.core.OFFER_TYPE;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ItemDescModelImpl implements ItemDescModel {
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(ItemDescModelImpl.class);
|
||||||
|
|
||||||
|
protected final ItemModel item;
|
||||||
|
protected final ItemStatModel statSell;
|
||||||
|
protected final ItemStatModel statBuy;
|
||||||
|
|
||||||
|
public ItemDescModelImpl(ItemModel item, ItemStatModel statSell, ItemStatModel statBuy) {
|
||||||
|
this.item = item;
|
||||||
|
this.statSell = statSell;
|
||||||
|
this.statBuy = statBuy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemModel getItem(){
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadOnlyStringProperty nameProperty() {
|
||||||
|
return item.nameProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadOnlyDoubleProperty avgBuyProperty() {
|
||||||
|
return statBuy.avgProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadOnlyObjectProperty<OfferModel> minBuyProperty() {
|
||||||
|
return statBuy.minProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadOnlyObjectProperty<OfferModel> maxBuyProperty() {
|
||||||
|
return statBuy.maxProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadOnlyObjectProperty<OfferModel> bestBuyProperty() {
|
||||||
|
return statBuy.bestProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadOnlyDoubleProperty avgSellProperty() {
|
||||||
|
return statSell.avgProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadOnlyObjectProperty<OfferModel> minSellProperty() {
|
||||||
|
return statSell.minProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadOnlyObjectProperty<OfferModel> maxSellProperty() {
|
||||||
|
return statSell.maxProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ReadOnlyObjectProperty<OfferModel> bestSellProperty() {
|
||||||
|
return statSell.bestProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<OfferModel> getSeller() {
|
||||||
|
return statSell.getOffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<OfferModel> getBuyer() {
|
||||||
|
return statBuy.getOffers();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refresh(){
|
||||||
|
LOG.trace("Refresh stats of itemDesc {}", this);
|
||||||
|
statBuy.refresh();
|
||||||
|
statSell.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void refresh(OFFER_TYPE type){
|
||||||
|
LOG.trace("Refresh {} stat of itemDesc {}", type, this);
|
||||||
|
switch (type) {
|
||||||
|
case SELL: statSell.refresh();
|
||||||
|
break;
|
||||||
|
case BUY: statBuy.refresh();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasItem(ItemModel item){
|
||||||
|
return this.item.getItem().equals(item.getItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasItem(OfferModel offer){
|
||||||
|
return this.item.getItem().equals(offer.getOffer().getItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return item.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
52
client/src/main/java/ru/trader/model/ItemModel.java
Normal file
52
client/src/main/java/ru/trader/model/ItemModel.java
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package ru.trader.model;
|
||||||
|
|
||||||
|
import javafx.beans.property.ReadOnlyStringProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import ru.trader.core.Item;
|
||||||
|
|
||||||
|
public class ItemModel{
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(ItemModel.class);
|
||||||
|
private final Item item;
|
||||||
|
private final MarketModel market;
|
||||||
|
private StringProperty name;
|
||||||
|
|
||||||
|
ItemModel(Item item, MarketModel market) {
|
||||||
|
this.item = item;
|
||||||
|
this.market = market;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {return name != null ? name.get() : item.getName();}
|
||||||
|
|
||||||
|
public void setName(String value) {
|
||||||
|
LOG.info("Change name of item {} to {}", item, name);
|
||||||
|
market.updateName(this, value);
|
||||||
|
if (name != null) name.set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyStringProperty nameProperty() {
|
||||||
|
if (name == null) {
|
||||||
|
name = new SimpleStringProperty(item.getName());
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (LOG.isTraceEnabled()){
|
||||||
|
final StringBuilder sb = new StringBuilder("ItemModel{");
|
||||||
|
sb.append("nameProp=").append(name);
|
||||||
|
sb.append(", item=").append(super.toString());
|
||||||
|
sb.append('}');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
return item.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Item getItem() {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
116
client/src/main/java/ru/trader/model/ItemStatModel.java
Normal file
116
client/src/main/java/ru/trader/model/ItemStatModel.java
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
package ru.trader.model;
|
||||||
|
|
||||||
|
import javafx.beans.property.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import ru.trader.core.ItemStat;
|
||||||
|
import ru.trader.core.Offer;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ItemStatModel {
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(ItemStatModel.class);
|
||||||
|
private ItemStat itemStat;
|
||||||
|
private final MarketModel market;
|
||||||
|
|
||||||
|
private DoubleProperty avg;
|
||||||
|
private ObjectProperty<OfferModel> max;
|
||||||
|
private ObjectProperty<OfferModel> min;
|
||||||
|
private ObjectProperty<OfferModel> best;
|
||||||
|
|
||||||
|
ItemStatModel(ItemStat itemStat, MarketModel market) {
|
||||||
|
this.itemStat = itemStat;
|
||||||
|
this.market = market;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyDoubleProperty avgProperty(){
|
||||||
|
if (avg == null) avg = new SimpleDoubleProperty(itemStat.getAvg());
|
||||||
|
return avg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyObjectProperty<OfferModel> minProperty(){
|
||||||
|
if (min == null){
|
||||||
|
min = new SimpleObjectProperty<>(market.asModel(itemStat.getMin()));
|
||||||
|
}
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyObjectProperty<OfferModel> maxProperty(){
|
||||||
|
if (max == null) {
|
||||||
|
max = new SimpleObjectProperty<>(market.asModel(itemStat.getMax()));
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyObjectProperty<OfferModel> bestProperty(){
|
||||||
|
if (best == null){
|
||||||
|
best = new SimpleObjectProperty<>(market.asModel(itemStat.getBest()));
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getAvg() {
|
||||||
|
return avg != null ? avg.get() : itemStat.getAvg();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OfferModel getMax() {
|
||||||
|
return max != null ? max.get() : market.asModel(itemStat.getMax());
|
||||||
|
}
|
||||||
|
|
||||||
|
public OfferModel getMin() {
|
||||||
|
return min != null ? min.get() : market.asModel(itemStat.getMin());
|
||||||
|
}
|
||||||
|
|
||||||
|
public OfferModel getBest() {
|
||||||
|
return best != null ? best.get() : market.asModel(itemStat.getBest());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OfferModel> getOffers() {
|
||||||
|
return itemStat.getOffers().stream().map(market::asModel).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuilder sb = new StringBuilder("");
|
||||||
|
if (LOG.isTraceEnabled()){
|
||||||
|
sb.append("ItemStatModel{");
|
||||||
|
sb.append("avgProp=").append(avg);
|
||||||
|
sb.append(", maxProp=").append(max);
|
||||||
|
sb.append(", minProp=").append(min);
|
||||||
|
sb.append(", bestProp=").append(best);
|
||||||
|
sb.append(", itemStat=").append(itemStat.toString());
|
||||||
|
sb.append('}');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
sb.append("avg=").append(getAvg());
|
||||||
|
sb.append(", max=").append(getMax());
|
||||||
|
sb.append(", min=").append(getMin());
|
||||||
|
sb.append(", best=").append(getBest());
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refresh(){
|
||||||
|
LOG.debug("Refresh model {}", this);
|
||||||
|
if (itemStat.isEmpty()) {
|
||||||
|
ItemStat fresh = market.getStat(itemStat.getType(), itemStat.getItem());
|
||||||
|
if (itemStat != fresh) itemStat = fresh;
|
||||||
|
}
|
||||||
|
if (avg!=null) avg.setValue(itemStat.getAvg());
|
||||||
|
refreshProp(min, itemStat.getMin());
|
||||||
|
refreshProp(max, itemStat.getMax());
|
||||||
|
refreshProp(best, itemStat.getBest());
|
||||||
|
LOG.debug("Fresh model = {}", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshProp(ObjectProperty<OfferModel> prop, Offer offer){
|
||||||
|
if (prop!=null ){
|
||||||
|
OfferModel model = prop.getValue();
|
||||||
|
if (model==null || !model.isModel(offer)){
|
||||||
|
prop.setValue(market.asModel(offer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
161
client/src/main/java/ru/trader/model/MarketModel.java
Normal file
161
client/src/main/java/ru/trader/model/MarketModel.java
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
package ru.trader.model;
|
||||||
|
|
||||||
|
import javafx.beans.property.ListProperty;
|
||||||
|
import javafx.beans.property.ReadOnlyListProperty;
|
||||||
|
import javafx.beans.property.SimpleListProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import ru.trader.core.*;
|
||||||
|
import ru.trader.model.support.BindingsHelper;
|
||||||
|
import ru.trader.model.support.ChangeMarketListener;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
|
||||||
|
public class MarketModel {
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(MarketModel.class);
|
||||||
|
|
||||||
|
private final Market market;
|
||||||
|
|
||||||
|
private final Collection<ChangeMarketListener> listener = new ArrayList<>();
|
||||||
|
|
||||||
|
private final ListProperty<Vendor> vendors;
|
||||||
|
private final ListProperty<ItemDescModel> items;
|
||||||
|
|
||||||
|
private boolean alert = true;
|
||||||
|
|
||||||
|
public ReadOnlyListProperty<Vendor> vendorsProperty() {
|
||||||
|
return vendors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyListProperty<ItemDescModel> itemsProperty() {
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAlert(boolean alert) {
|
||||||
|
this.alert = alert;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MarketModel(Market market) {
|
||||||
|
this.market = market;
|
||||||
|
items = new SimpleListProperty<>(BindingsHelper.observableList(market.getItems(), this::getItemDesc));
|
||||||
|
vendors = new SimpleListProperty<>(FXCollections.observableArrayList(market.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addListener(ChangeMarketListener listener){
|
||||||
|
synchronized (this.listener){
|
||||||
|
this.listener.add(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeListeners() {
|
||||||
|
synchronized (listener){
|
||||||
|
listener.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAllListener(Collection<? extends ChangeMarketListener> listener){
|
||||||
|
synchronized (this.listener){
|
||||||
|
this.listener.addAll(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<ChangeMarketListener> getListeners() {
|
||||||
|
return listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateName(ItemModel model, String value) {
|
||||||
|
Item item = model.getItem();
|
||||||
|
String old = item.getName();
|
||||||
|
item.setName(value);
|
||||||
|
if (alert) listener.forEach((c) -> c.nameChange(model, old, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateName(VendorModel model, String value) {
|
||||||
|
Vendor vendor = model.getVendor();
|
||||||
|
String old = vendor.getName();
|
||||||
|
vendor.setName(value);
|
||||||
|
if (alert) listener.forEach((c) -> c.nameChange(model, old, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void updatePrice(OfferModel model, double value) {
|
||||||
|
Offer offer = model.getOffer();
|
||||||
|
double old = offer.getPrice();
|
||||||
|
market.updatePrice(offer, value);
|
||||||
|
if (alert) listener.forEach((c) -> c.priceChange(model, old, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(VendorModel vendor, OfferModel offer) {
|
||||||
|
market.add(vendor.getVendor(), offer.getOffer());
|
||||||
|
if (alert) listener.forEach((c) -> c.add(offer));
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(VendorModel vendor, OfferModel offer) {
|
||||||
|
market.remove(vendor.getVendor(), offer.getOffer());
|
||||||
|
if (alert) listener.forEach((c) -> c.remove(offer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(VendorModel vendor) {
|
||||||
|
LOG.info("Add vendor {} to market {}", vendor, this);
|
||||||
|
market.add(vendor.getVendor());
|
||||||
|
if (alert) listener.forEach((c) -> c.add(vendor));
|
||||||
|
vendors.add(vendor.getVendor());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(ItemModel item) {
|
||||||
|
LOG.info("Add item {} to market {}", item, this);
|
||||||
|
market.add(item.getItem());
|
||||||
|
ItemDescModel model = getItemDesc(item);
|
||||||
|
if (alert) listener.forEach((c) -> c.add(model));
|
||||||
|
items.add(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemModel newItem(String name){
|
||||||
|
return ModelFabrica.buildItemModel(name, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VendorModel newVendor(String name){
|
||||||
|
return ModelFabrica.buildModel(name, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OfferModel newOffer(OFFER_TYPE type, ItemModel item, double price) {
|
||||||
|
return ModelFabrica.buildModel(type, item, price, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemDescModel getItemDesc(Item item){
|
||||||
|
return getItemDesc(asModel(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemDescModel getItemDesc(ItemModel item){
|
||||||
|
return ModelFabrica.buildModel(item, market.getStatSell(item.getItem()), market.getStatBuy(item.getItem()), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OfferDescModel asOfferDescModel(Offer offer){
|
||||||
|
return asOfferDescModel(asModel(offer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public OfferDescModel asOfferDescModel(OfferModel offer){
|
||||||
|
Item item = offer.getOffer().getItem();
|
||||||
|
return ModelFabrica.buildModel(offer, market.getStatSell(item), market.getStatBuy(item), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
ItemStat getStat(OFFER_TYPE type, Item item){
|
||||||
|
return market.getStat(type, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OfferModel asModel(Offer offer){
|
||||||
|
return ModelFabrica.getModel(offer, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemModel asModel(Item item){
|
||||||
|
return ModelFabrica.getModel(item, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VendorModel asModel(Vendor vendor) {
|
||||||
|
return ModelFabrica.getModel(vendor, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
101
client/src/main/java/ru/trader/model/ModelFabrica.java
Normal file
101
client/src/main/java/ru/trader/model/ModelFabrica.java
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
package ru.trader.model;
|
||||||
|
|
||||||
|
import ru.trader.core.*;
|
||||||
|
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class ModelFabrica {
|
||||||
|
|
||||||
|
private static final HashMap<Item, WeakReference<ItemModel>> items = new HashMap<>();
|
||||||
|
private static final HashMap<Vendor, WeakReference<VendorModel>> vendors = new HashMap<>();
|
||||||
|
private static final HashMap<Offer, WeakReference<OfferModel>> offers = new HashMap<>();
|
||||||
|
|
||||||
|
private static final HashMap<ItemStat, WeakReference<ItemStatModel>> stats = new HashMap<>();
|
||||||
|
|
||||||
|
public static ItemModel buildItemModel(String name, MarketModel market){
|
||||||
|
return getModel(new Item(name), market);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static VendorModel buildModel(String name, MarketModel market){
|
||||||
|
return getModel(new SimpleVendor(name), market);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OfferModel buildModel(OFFER_TYPE type, ItemModel item, double price, MarketModel market) {
|
||||||
|
return getModel(new Offer(type, item.getItem(), price), market);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemDescModel buildModel(ItemModel item, ItemStat sell, ItemStat buy, MarketModel market) {
|
||||||
|
return new ItemDescModelImpl(item, getModel(sell, market), getModel(buy, market));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OfferDescModel buildModel(OfferModel offer, ItemStat sell, ItemStat buy, MarketModel market){
|
||||||
|
return new OfferDescModel(offer, getModel(sell, market), getModel(buy, market));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static VendorModel getModel(Vendor vendor, MarketModel market){
|
||||||
|
if (vendor == null) return null;
|
||||||
|
VendorModel res=null;
|
||||||
|
WeakReference<VendorModel> ref = vendors.get(vendor);
|
||||||
|
if (ref != null){
|
||||||
|
res = ref.get();
|
||||||
|
}
|
||||||
|
if (res == null){
|
||||||
|
res = new VendorModel(vendor, market);
|
||||||
|
vendors.put(vendor, new WeakReference<>(res));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static ItemModel getModel(Item item, MarketModel market){
|
||||||
|
if (item == null) return null;
|
||||||
|
ItemModel res=null;
|
||||||
|
WeakReference<ItemModel> ref = items.get(item);
|
||||||
|
if (ref != null){
|
||||||
|
res = ref.get();
|
||||||
|
}
|
||||||
|
if (res == null){
|
||||||
|
res = new ItemModel(item, market);
|
||||||
|
items.put(item, new WeakReference<>(res));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static OfferModel getModel(Offer offer, MarketModel market){
|
||||||
|
if (offer == null) return null;
|
||||||
|
OfferModel res = null;
|
||||||
|
WeakReference<OfferModel> ref = offers.get(offer);
|
||||||
|
if (ref != null){
|
||||||
|
res = ref.get();
|
||||||
|
}
|
||||||
|
if (res == null){
|
||||||
|
res = new OfferModel(offer, market);
|
||||||
|
offers.put(offer, new WeakReference<>(res));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStatModel getModel(ItemStat itemStat, MarketModel market){
|
||||||
|
if (itemStat == null) return null;
|
||||||
|
ItemStatModel res = null;
|
||||||
|
WeakReference<ItemStatModel> ref = stats.get(itemStat);
|
||||||
|
if (ref != null){
|
||||||
|
res = ref.get();
|
||||||
|
}
|
||||||
|
if (res == null){
|
||||||
|
res = new ItemStatModel(itemStat, market);
|
||||||
|
stats.put(itemStat, new WeakReference<>(res));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear(){
|
||||||
|
items.clear();
|
||||||
|
vendors.clear();
|
||||||
|
offers.clear();
|
||||||
|
stats.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
93
client/src/main/java/ru/trader/model/OfferDescModel.java
Normal file
93
client/src/main/java/ru/trader/model/OfferDescModel.java
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package ru.trader.model;
|
||||||
|
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.property.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import ru.trader.model.support.ModelBindings;
|
||||||
|
|
||||||
|
public class OfferDescModel extends ItemDescModelImpl implements ItemDescModel{
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(OfferDescModel.class);
|
||||||
|
|
||||||
|
protected DoubleProperty diff;
|
||||||
|
protected final OfferModel offer;
|
||||||
|
protected DoubleProperty maxProfit;
|
||||||
|
protected DoubleProperty minProfit;
|
||||||
|
protected DoubleProperty avgProfit;
|
||||||
|
|
||||||
|
public OfferDescModel(OfferModel offer, ItemStatModel statSell, ItemStatModel statBuy) {
|
||||||
|
super(offer.getItem(), statSell, statBuy);
|
||||||
|
this.offer = offer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyDoubleProperty priceProperty() {
|
||||||
|
return offer.priceProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getPrice(){
|
||||||
|
return offer.getPrice();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyDoubleProperty profitProperty() {
|
||||||
|
if (maxProfit == null){
|
||||||
|
maxProfit = new SimpleDoubleProperty(0);
|
||||||
|
switch (offer.getType()) {
|
||||||
|
case SELL: maxProfit.bind(ModelBindings.diff(bestBuyProperty(), priceProperty()));
|
||||||
|
break;
|
||||||
|
case BUY: maxProfit.bind(ModelBindings.diff(priceProperty(), bestSellProperty()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maxProfit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyDoubleProperty avgProfitProperty() {
|
||||||
|
if (avgProfit == null){
|
||||||
|
avgProfit = new SimpleDoubleProperty(0);
|
||||||
|
switch (offer.getType()) {
|
||||||
|
case SELL: avgProfit.bind(avgBuyProperty().subtract(priceProperty()));
|
||||||
|
break;
|
||||||
|
case BUY: avgProfit.bind(priceProperty().subtract(avgSellProperty()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return avgProfit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyDoubleProperty minProfitProperty() {
|
||||||
|
if (minProfit == null){
|
||||||
|
minProfit = new SimpleDoubleProperty(0);
|
||||||
|
switch (offer.getType()) {
|
||||||
|
case SELL: minProfit.bind(ModelBindings.diff(minBuyProperty(), priceProperty()));
|
||||||
|
break;
|
||||||
|
case BUY: minProfit.bind(ModelBindings.diff(priceProperty(), minSellProperty()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return minProfit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyDoubleProperty diffProperty(){
|
||||||
|
if (diff == null){
|
||||||
|
diff = new SimpleDoubleProperty(0);
|
||||||
|
switch (offer.getType()) {
|
||||||
|
case SELL: diff.bind(Bindings.subtract(priceProperty(), avgSellProperty()));
|
||||||
|
break;
|
||||||
|
case BUY: diff.bind(Bindings.subtract(priceProperty(), avgBuyProperty()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getDiff(){
|
||||||
|
return diffProperty().get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public OfferModel getOffer(){
|
||||||
|
return offer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
103
client/src/main/java/ru/trader/model/OfferModel.java
Normal file
103
client/src/main/java/ru/trader/model/OfferModel.java
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package ru.trader.model;
|
||||||
|
|
||||||
|
import javafx.beans.property.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import ru.trader.core.OFFER_TYPE;
|
||||||
|
import ru.trader.core.Offer;
|
||||||
|
|
||||||
|
public class OfferModel{
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(OfferModel.class);
|
||||||
|
private final MarketModel market;
|
||||||
|
private final Offer offer;
|
||||||
|
|
||||||
|
private DoubleProperty price;
|
||||||
|
|
||||||
|
|
||||||
|
OfferModel(Offer offer, MarketModel market) {
|
||||||
|
this.market = market;
|
||||||
|
this.offer = offer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public double getPrice() {return price != null ? price.get() : offer.getPrice();}
|
||||||
|
|
||||||
|
public void setPrice(double value) {
|
||||||
|
if (getPrice() == value) return;
|
||||||
|
LOG.info("Change price offer {} to {}", offer, value);
|
||||||
|
market.updatePrice(this, value);
|
||||||
|
if (price != null) price.set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyDoubleProperty priceProperty() {
|
||||||
|
if (price == null) {
|
||||||
|
price = new SimpleDoubleProperty(offer.getPrice());
|
||||||
|
}
|
||||||
|
return price;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ItemModel getItem() {
|
||||||
|
return market.asModel(offer.getItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
public VendorModel getVendor() {
|
||||||
|
return market.asModel(offer.getVendor());
|
||||||
|
}
|
||||||
|
|
||||||
|
public OFFER_TYPE getType(){
|
||||||
|
return offer.getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isModel(Offer offer) {
|
||||||
|
return this.offer == offer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasVendor(VendorModel vendor){
|
||||||
|
return offer.getVendor().equals(vendor.getVendor());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasItem(ItemModel item){
|
||||||
|
return offer.getItem().equals(item.getItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof OfferModel)) return false;
|
||||||
|
|
||||||
|
OfferModel that = (OfferModel) o;
|
||||||
|
|
||||||
|
return offer.equals(that.offer);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return offer.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (LOG.isTraceEnabled()){
|
||||||
|
final StringBuilder sb = new StringBuilder("OfferModel{");
|
||||||
|
sb.append("priceProp=").append(price);
|
||||||
|
sb.append(", offer=").append(offer.toString());
|
||||||
|
sb.append('}');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
return offer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
Offer getOffer() {
|
||||||
|
return offer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toVString(){
|
||||||
|
return offer.toVString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toIString(){
|
||||||
|
return offer.toIString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
109
client/src/main/java/ru/trader/model/OrderModel.java
Normal file
109
client/src/main/java/ru/trader/model/OrderModel.java
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
package ru.trader.model;
|
||||||
|
|
||||||
|
import javafx.beans.property.*;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import ru.trader.model.support.ModelBindings;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class OrderModel {
|
||||||
|
|
||||||
|
private final OfferDescModel offer;
|
||||||
|
|
||||||
|
private final LongProperty count;
|
||||||
|
private final ObjectProperty<OfferModel> buyer = new SimpleObjectProperty<>();
|
||||||
|
private long max;
|
||||||
|
private DoubleProperty profit;
|
||||||
|
private DoubleProperty bestProfit;
|
||||||
|
|
||||||
|
public OrderModel(OfferDescModel offer) {
|
||||||
|
this.offer = offer;
|
||||||
|
this.count = new SimpleLongProperty(0){
|
||||||
|
@Override
|
||||||
|
public void setValue(Number v) {
|
||||||
|
if (max > 0 && v.longValue() > max){
|
||||||
|
super.setValue(max);
|
||||||
|
} else {
|
||||||
|
super.setValue(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrderModel(OfferDescModel offer, double balance, long limit) {
|
||||||
|
this(offer);
|
||||||
|
this.max = Math.min(limit, (long) Math.floor(balance / offer.getPrice()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public OfferModel getOffer() {
|
||||||
|
return offer.getOffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyStringProperty nameProperty() {
|
||||||
|
return offer.nameProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyDoubleProperty priceProperty() {
|
||||||
|
return offer.priceProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyObjectProperty<OfferModel> bestProperty(){
|
||||||
|
return offer.bestBuyProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getCount() {
|
||||||
|
return count.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public LongProperty countProperty() {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCount(long count) {
|
||||||
|
this.count.set(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyDoubleProperty profitProperty() {
|
||||||
|
if (profit == null){
|
||||||
|
profit = new SimpleDoubleProperty(0);
|
||||||
|
profit.bind(ModelBindings.diff(buyer, offer.getOffer().priceProperty()).multiply(count));
|
||||||
|
}
|
||||||
|
return profit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyDoubleProperty bestProfitProperty() {
|
||||||
|
if (bestProfit == null){
|
||||||
|
bestProfit = new SimpleDoubleProperty(0);
|
||||||
|
bestProfit.bind(offer.profitProperty().multiply(max));
|
||||||
|
}
|
||||||
|
return bestProfit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableValue<Double> getProfit(OfferModel buyer) {
|
||||||
|
return buyer.priceProperty().subtract(offer.getOffer().priceProperty()).multiply(max).asObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<OfferModel> buyerProperty() {
|
||||||
|
return buyer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBuyer(OfferModel buyer) {
|
||||||
|
this.buyer.set(buyer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OfferModel getBuyer() {
|
||||||
|
return buyer.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getMax() {
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMax(long max) {
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OfferModel> getBuyers(){
|
||||||
|
return offer.getBuyer();
|
||||||
|
}
|
||||||
|
}
|
||||||
98
client/src/main/java/ru/trader/model/VendorModel.java
Normal file
98
client/src/main/java/ru/trader/model/VendorModel.java
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package ru.trader.model;
|
||||||
|
|
||||||
|
import javafx.beans.property.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import ru.trader.core.Vendor;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class VendorModel {
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(VendorModel.class);
|
||||||
|
private final Vendor vendor;
|
||||||
|
private final MarketModel market;
|
||||||
|
private StringProperty name;
|
||||||
|
|
||||||
|
VendorModel(Vendor vendor, MarketModel market) {
|
||||||
|
this.vendor = vendor;
|
||||||
|
this.market = market;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {return name != null ? name.get() : vendor.getName();}
|
||||||
|
|
||||||
|
public void setName(String value) {
|
||||||
|
if (getName().equals(value)) return;
|
||||||
|
LOG.info("Change name vendor {} to {}", vendor, value);
|
||||||
|
market.updateName(this, value);
|
||||||
|
if (name != null) name.set(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyStringProperty nameProperty() {
|
||||||
|
if (name == null) {
|
||||||
|
name = new SimpleStringProperty(vendor.getName());
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OfferModel> getSells() {
|
||||||
|
return vendor.getAllSellOffers().stream().map(market::asModel).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OfferModel> getBuys() {
|
||||||
|
return vendor.getAllBuyOffers().stream().map(market::asModel).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> List<T> getSells(Function<OfferModel, T> mapper) {
|
||||||
|
return vendor.getAllSellOffers().stream().map(market::asModel).map(mapper).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> List<T> getBuys(Function<OfferModel, T> mapper) {
|
||||||
|
return vendor.getAllBuyOffers().stream().map(market::asModel).map(mapper).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Vendor getVendor() {
|
||||||
|
return vendor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(OfferModel offer){
|
||||||
|
LOG.info("Add offer {} to vendor {}", offer, vendor);
|
||||||
|
market.add(this, offer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(OfferModel offer) {
|
||||||
|
LOG.info("Remove offer {} from vendor {}", offer, vendor);
|
||||||
|
market.remove(this, offer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasSell(ItemModel item) {
|
||||||
|
return vendor.hasSell(item.getItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasBuy(ItemModel item) {
|
||||||
|
return vendor.hasBuy(item.getItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (LOG.isTraceEnabled()){
|
||||||
|
final StringBuilder sb = new StringBuilder("VendorModel{");
|
||||||
|
sb.append("nameProp=").append(name);
|
||||||
|
sb.append(", vendor=").append(vendor.toString());
|
||||||
|
sb.append('}');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
return vendor.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<OfferModel> getSell(ItemModel item){
|
||||||
|
return Optional.ofNullable(market.asModel(vendor.getSell(item.getItem())));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<OfferModel> getBuy(ItemModel item){
|
||||||
|
return Optional.ofNullable(market.asModel(vendor.getBuy(item.getItem())));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
package ru.trader.model.support;
|
||||||
|
|
||||||
|
import com.sun.javafx.collections.ImmutableObservableList;
|
||||||
|
import javafx.beans.Observable;
|
||||||
|
import javafx.beans.binding.ListBinding;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ListChangeListener;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class BindingsHelper {
|
||||||
|
|
||||||
|
public static <T,M extends Observable> ObservableList<M> deepObservableList(Collection<T> entries, Function<T, M> convert){
|
||||||
|
return deepBind(observableList(entries, convert));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T,M> ObservableList<M> observableList(Collection<T> entries, Function<T, M> convert){
|
||||||
|
List<M> list = new ArrayList<>(entries.size());
|
||||||
|
entries.forEach((v)->list.add(convert.apply(v)));
|
||||||
|
return FXCollections.observableList(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends Observable> ObservableList<T> deepBind(final ObservableList<T> list){
|
||||||
|
if ((list == null)) {
|
||||||
|
throw new NullPointerException("Operands cannot be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ListBinding<T>() {
|
||||||
|
{
|
||||||
|
super.bind(list);
|
||||||
|
list.addListener((ListChangeListener<T>) c -> {
|
||||||
|
while (c.next()) {
|
||||||
|
if (c.wasAdded())
|
||||||
|
for (T item : c.getRemoved()) {
|
||||||
|
super.bind(item);
|
||||||
|
} else if (c.wasRemoved()){
|
||||||
|
for (T item : c.getAddedSubList()) {
|
||||||
|
super.unbind(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
list.forEach((v) -> super.unbind(v));
|
||||||
|
super.unbind(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ObservableList<T> computeValue() {
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public javafx.collections.ObservableList<?> getDependencies() {
|
||||||
|
ArrayList<Observable> dependencies = new ArrayList<>(list.size());
|
||||||
|
dependencies.addAll(list);
|
||||||
|
dependencies.add(list);
|
||||||
|
return new ImmutableObservableList<>((Observable[]) dependencies.toArray());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package ru.trader.model.support;
|
||||||
|
|
||||||
|
import ru.trader.model.ItemDescModel;
|
||||||
|
import ru.trader.model.ItemModel;
|
||||||
|
import ru.trader.model.OfferModel;
|
||||||
|
import ru.trader.model.VendorModel;
|
||||||
|
|
||||||
|
public class ChangeMarketListener {
|
||||||
|
|
||||||
|
public void nameChange(ItemModel item, String oldName, String newName){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void nameChange(VendorModel vendor, String oldName, String newName) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(ItemDescModel item) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void priceChange(OfferModel offer, double oldPrice, double newPrice) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(OfferModel offer) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(VendorModel vendor) {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(OfferModel offer) {
|
||||||
|
}
|
||||||
|
}
|
||||||
206
client/src/main/java/ru/trader/model/support/ModelBindings.java
Normal file
206
client/src/main/java/ru/trader/model/support/ModelBindings.java
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
package ru.trader.model.support;
|
||||||
|
|
||||||
|
import com.sun.javafx.collections.ImmutableObservableList;
|
||||||
|
import javafx.beans.Observable;
|
||||||
|
import javafx.beans.binding.*;
|
||||||
|
import javafx.beans.property.ReadOnlyDoubleProperty;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import ru.trader.model.ItemModel;
|
||||||
|
import ru.trader.model.OfferModel;
|
||||||
|
import ru.trader.model.VendorModel;
|
||||||
|
|
||||||
|
|
||||||
|
public class ModelBindings {
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(ModelBindings.class);
|
||||||
|
|
||||||
|
|
||||||
|
public static StringBinding asString(final OfferModel offer){
|
||||||
|
return Bindings.createStringBinding(offer::toVString, offer.priceProperty(), offer.getVendor().nameProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StringBinding asItemString(final OfferModel offer){
|
||||||
|
return Bindings.createStringBinding(offer::toIString, offer.priceProperty(), offer.getItem().nameProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DoubleBinding price(final ObservableValue<OfferModel> offer){
|
||||||
|
ObservableValue<OfferModel> offerBind = offerPrice(offer);
|
||||||
|
return asDouble(offerBind, offerBind);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DoubleBinding diff(final ObservableValue<OfferModel> of1, final ObservableValue<OfferModel> of2){
|
||||||
|
return price(of1).subtract(price(of2));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DoubleBinding diff(final ReadOnlyDoubleProperty price, final ObservableValue<OfferModel> offer){
|
||||||
|
return diff(offer, price).negate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DoubleBinding diff(final ObservableValue<OfferModel> offer, final ReadOnlyDoubleProperty price){
|
||||||
|
return price(offer).subtract(price);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StringBinding asString(final ObservableValue<OfferModel> offer){
|
||||||
|
ObservableValue<OfferModel> offerBind = offerPrice(offer, true);
|
||||||
|
return asString(offerBind, offerBind);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static StringBinding asItemString(final ObservableValue<OfferModel> offer){
|
||||||
|
ObservableValue<OfferModel> offerBind = offerPrice(offer, true);
|
||||||
|
return asString(offerBind, offerBind);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private static StringBinding asItemString(final ObservableValue<OfferModel> offer, final Observable... dependencies){
|
||||||
|
return Bindings.createStringBinding(() -> {
|
||||||
|
OfferModel o = offer.getValue();
|
||||||
|
return o != null ? o.toIString(): "";
|
||||||
|
}, dependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static StringBinding asString(final ObservableValue<OfferModel> offer, final Observable... dependencies){
|
||||||
|
return Bindings.createStringBinding(() -> {
|
||||||
|
OfferModel o = offer.getValue();
|
||||||
|
return o != null ? o.toVString() : "";
|
||||||
|
}, dependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DoubleBinding asDouble(final ObservableValue<OfferModel> offer, final Observable... dependencies){
|
||||||
|
return Bindings.createDoubleBinding(() -> {
|
||||||
|
OfferModel o = offer.getValue();
|
||||||
|
return o != null ? offer.getValue().getPrice() : Double.NaN;
|
||||||
|
}, dependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ObservableValue<OfferModel> offerPrice(final ObservableValue<OfferModel> offer){
|
||||||
|
return offerPrice(offer, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ObservableValue<OfferModel> offerPrice(final ObservableValue<OfferModel> offer, final boolean deep){
|
||||||
|
if ((offer == null)) {
|
||||||
|
throw new NullPointerException("Operands cannot be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ObjectBinding<OfferModel>() {
|
||||||
|
{
|
||||||
|
super.bind(offer);
|
||||||
|
bind(offer.getValue());
|
||||||
|
offer.addListener((observable, oldValue, newValue) -> {
|
||||||
|
LOG.trace("unbind {}, bind {}", oldValue, newValue);
|
||||||
|
unbind(oldValue);
|
||||||
|
bind(newValue);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
unbind(offer.getValue());
|
||||||
|
super.unbind(offer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected OfferModel computeValue() {
|
||||||
|
return offer.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public javafx.collections.ObservableList<?> getDependencies() {
|
||||||
|
if (deep){
|
||||||
|
OfferModel model = offer.getValue();
|
||||||
|
return new ImmutableObservableList<Observable>(offer, model.priceProperty(), model.getVendor().nameProperty(), model.getItem().nameProperty());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return new ImmutableObservableList<Observable>(offer, offer.getValue().priceProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bind(OfferModel model){
|
||||||
|
if (model == null) return;
|
||||||
|
super.bind(model.priceProperty());
|
||||||
|
if (deep){
|
||||||
|
super.bind(model.getVendor().nameProperty());
|
||||||
|
super.bind(model.getItem().nameProperty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void unbind(OfferModel model){
|
||||||
|
if (model == null) return;
|
||||||
|
super.unbind(model.priceProperty());
|
||||||
|
if (deep){
|
||||||
|
super.unbind(model.getVendor().nameProperty());
|
||||||
|
super.unbind(model.getItem().nameProperty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ObservableValue<ItemModel> itemName(final ObservableValue<ItemModel> item){
|
||||||
|
if ((item == null)) {
|
||||||
|
throw new NullPointerException("Operands cannot be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ObjectBinding<ItemModel>() {
|
||||||
|
{
|
||||||
|
super.bind(item);
|
||||||
|
super.bind(item.getValue().nameProperty());
|
||||||
|
item.addListener((observable, oldValue, newValue) -> {
|
||||||
|
super.unbind(oldValue.nameProperty());
|
||||||
|
super.bind(newValue.nameProperty());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
super.unbind(item.getValue().nameProperty());
|
||||||
|
super.unbind(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ItemModel computeValue() {
|
||||||
|
return item.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public javafx.collections.ObservableList<?> getDependencies() {
|
||||||
|
return new ImmutableObservableList<Observable>(item, item.getValue().nameProperty());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static ObservableValue<VendorModel> vendorName(final ObservableValue<VendorModel> vendor){
|
||||||
|
if ((vendor == null)) {
|
||||||
|
throw new NullPointerException("Operands cannot be null.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ObjectBinding<VendorModel>() {
|
||||||
|
{
|
||||||
|
super.bind(vendor);
|
||||||
|
super.bind(vendor.getValue().nameProperty());
|
||||||
|
vendor.addListener((observable, oldValue, newValue) -> {
|
||||||
|
super.unbind(oldValue.nameProperty());
|
||||||
|
super.bind(newValue.nameProperty());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose() {
|
||||||
|
super.unbind(vendor.getValue().nameProperty());
|
||||||
|
super.unbind(vendor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected VendorModel computeValue() {
|
||||||
|
return vendor.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public javafx.collections.ObservableList<?> getDependencies() {
|
||||||
|
return new ImmutableObservableList<Observable>(vendor, vendor.getValue().nameProperty());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package ru.trader.view.support;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
|
public class NaNComparator<T extends Number> implements Comparator<T> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compare(Number n1, Number n2) {
|
||||||
|
double d1 = n1.doubleValue();
|
||||||
|
double d2 = n2.doubleValue();
|
||||||
|
boolean isNaN1 = Double.isNaN(d1);
|
||||||
|
boolean isNaN2 = Double.isNaN(d2);
|
||||||
|
if (isNaN1 && isNaN2) return 0;
|
||||||
|
if (isNaN1) return -1;
|
||||||
|
if (isNaN2) return 1;
|
||||||
|
|
||||||
|
return Double.compare(d1, d2);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
77
client/src/main/java/ru/trader/view/support/NumberField.java
Normal file
77
client/src/main/java/ru/trader/view/support/NumberField.java
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package ru.trader.view.support;
|
||||||
|
|
||||||
|
import javafx.beans.property.*;
|
||||||
|
import javafx.geometry.Point2D;
|
||||||
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.control.Tooltip;
|
||||||
|
import javafx.util.converter.NumberStringConverter;
|
||||||
|
|
||||||
|
|
||||||
|
public class NumberField extends TextField {
|
||||||
|
private final static NumberStringConverter converter = new NumberStringConverter("#0.#");
|
||||||
|
private final Tooltip tooltip = new Tooltip();
|
||||||
|
|
||||||
|
private final ObjectProperty<Number> number = new SimpleObjectProperty<>(0);
|
||||||
|
private final BooleanProperty wrong = new SimpleBooleanProperty(false);
|
||||||
|
|
||||||
|
public ObjectProperty<Number> numberProperty() {
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Number getValue(){
|
||||||
|
return number.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(Number value){
|
||||||
|
number.setValue(value);
|
||||||
|
setText(converter.toString(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isWrong() {
|
||||||
|
return wrong.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanProperty wrongProperty() {
|
||||||
|
return ReadOnlyBooleanWrapper.booleanProperty(wrong);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NumberField() {
|
||||||
|
super();
|
||||||
|
tooltip.setText("Wrong number");
|
||||||
|
tooltip.setAutoHide(true);
|
||||||
|
wrong.addListener((ob, o ,n) -> {
|
||||||
|
if (n) {
|
||||||
|
setTooltip(tooltip);
|
||||||
|
Point2D p = this.localToScene(0.0, 0.0);
|
||||||
|
tooltip.show(this, getScene().getWindow().getX() + getScene().getX() + p.getX(),
|
||||||
|
getScene().getWindow().getY() + getScene().getY() + p.getY() + getHeight() + 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
tooltip.hide();
|
||||||
|
setTooltip(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setOnAction((e) -> parseNumber());
|
||||||
|
focusedProperty().addListener((ob, o, n) -> {if (o) parseNumber();});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void parseNumber(){
|
||||||
|
String text = getText();
|
||||||
|
if (text == null || text.isEmpty()) {
|
||||||
|
number.setValue(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text.matches("^-?\\d+([,\\.]\\d+)?([eE]-?\\d+)?$")){
|
||||||
|
number.setValue(converter.fromString(text));
|
||||||
|
wrong.setValue(false);
|
||||||
|
} else {
|
||||||
|
wrong.setValue(true);
|
||||||
|
selectAll();
|
||||||
|
requestFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package ru.trader.view.support;
|
||||||
|
|
||||||
|
import com.sun.javafx.property.PropertyReference;
|
||||||
|
import javafx.beans.NamedArg;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class PropertyFactory <S,T> {
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(PropertyFactory.class);
|
||||||
|
|
||||||
|
private final String property;
|
||||||
|
|
||||||
|
private Class dataClass;
|
||||||
|
private String previousProperty;
|
||||||
|
protected PropertyReference<S> propertyRef;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a default PropertyValueFactory to extract the value from a given
|
||||||
|
* TableView row item reflectively, using the given property name.
|
||||||
|
*
|
||||||
|
* @param property The name of the property with which to attempt to
|
||||||
|
* reflectively extract a corresponding value for in a given object.
|
||||||
|
*/
|
||||||
|
public PropertyFactory(@NamedArg("property") String property) {
|
||||||
|
this.property = property;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the property name provided in the constructor.
|
||||||
|
*/
|
||||||
|
public final String getProperty() { return property; }
|
||||||
|
|
||||||
|
public final PropertyReference<S> getPropertyRef(T data){
|
||||||
|
return fillProperty(data)? propertyRef : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean fillProperty(T data) {
|
||||||
|
if (getProperty() == null || getProperty().isEmpty() || data == null) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// we attempt to cache the property reference here, as otherwise
|
||||||
|
// performance suffers when working in large data models. For
|
||||||
|
// a bit of reference, refer to RT-13937.
|
||||||
|
if (dataClass == null || previousProperty == null ||
|
||||||
|
! dataClass.equals(data.getClass()) ||
|
||||||
|
! previousProperty.equals(getProperty())) {
|
||||||
|
|
||||||
|
// create a new PropertyReference
|
||||||
|
this.dataClass = data.getClass();
|
||||||
|
this.previousProperty = getProperty();
|
||||||
|
this.propertyRef = new PropertyReference<>(data.getClass(), getProperty());
|
||||||
|
}
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
// log the warning and move on
|
||||||
|
LOG.warn("Can not retrieve property '{}' in PropertyValueFactory: {} with provided class type: {}", getProperty(), this, data.getClass());
|
||||||
|
LOG.warn("",e);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package ru.trader.view.support.cells;
|
||||||
|
|
||||||
|
import javafx.scene.control.TableCell;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
import ru.trader.view.support.NaNComparator;
|
||||||
|
|
||||||
|
public class DoubleCell<T> implements Callback<TableColumn<T, Double>, TableCell<T, Double>> {
|
||||||
|
@Override
|
||||||
|
public TableCell<T, Double> call(TableColumn<T, Double> param) {
|
||||||
|
param.setComparator(new NaNComparator<>());
|
||||||
|
return new TableCell<T, Double>(){
|
||||||
|
@Override
|
||||||
|
protected void updateItem(Double item, boolean empty) {
|
||||||
|
super.updateItem(item, empty);
|
||||||
|
if (empty) {
|
||||||
|
setText(null);
|
||||||
|
setGraphic(null);
|
||||||
|
} else {
|
||||||
|
setText(String.format("%.0f", item));
|
||||||
|
setGraphic(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package ru.trader.view.support.cells;
|
||||||
|
|
||||||
|
import javafx.scene.control.cell.TextFieldTableCell;
|
||||||
|
import javafx.util.StringConverter;
|
||||||
|
|
||||||
|
public abstract class EditingCell<S, T> extends TextFieldTableCell<S, T> {
|
||||||
|
|
||||||
|
|
||||||
|
protected EditingCell(StringConverter<T> converter) {
|
||||||
|
super(converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateItem(T item, boolean empty) {
|
||||||
|
super.updateItem(item, empty);
|
||||||
|
if (!empty && !isEditing()){
|
||||||
|
outText();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void outText();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelEdit() {
|
||||||
|
super.cancelEdit();
|
||||||
|
outText();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package ru.trader.view.support.cells;
|
||||||
|
|
||||||
|
import javafx.beans.NamedArg;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import ru.trader.model.OfferModel;
|
||||||
|
import ru.trader.model.support.ModelBindings;
|
||||||
|
|
||||||
|
|
||||||
|
public class OfferCellValueImpl<T> extends PropertyCellValueFactory<OfferModel, T, String>{
|
||||||
|
|
||||||
|
public OfferCellValueImpl(@NamedArg("property") String property) {
|
||||||
|
super(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
ObservableValue<String> format(ObservableValue<OfferModel> value) {
|
||||||
|
return ModelBindings.asString(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package ru.trader.view.support.cells;
|
||||||
|
|
||||||
|
import javafx.scene.control.ListCell;
|
||||||
|
import javafx.scene.control.ListView;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
import ru.trader.model.OfferModel;
|
||||||
|
import ru.trader.model.support.ModelBindings;
|
||||||
|
|
||||||
|
public class OfferListCell implements Callback<ListView<OfferModel>, ListCell<OfferModel>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListCell<OfferModel> call(ListView<OfferModel> param){
|
||||||
|
return new ListCell<OfferModel>(){
|
||||||
|
private OfferModel o;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateItem(OfferModel offer, boolean empty) {
|
||||||
|
super.updateItem(offer, empty);
|
||||||
|
if (!empty){
|
||||||
|
if (o != offer){
|
||||||
|
textProperty().unbind();
|
||||||
|
textProperty().bind(ModelBindings.asString(offer));
|
||||||
|
o = offer;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
textProperty().unbind();
|
||||||
|
o = null;
|
||||||
|
setText(null);
|
||||||
|
setGraphic(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package ru.trader.view.support.cells;
|
||||||
|
|
||||||
|
import javafx.scene.control.TableCell;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
import ru.trader.model.OfferModel;
|
||||||
|
import ru.trader.model.support.ModelBindings;
|
||||||
|
|
||||||
|
public class OfferTableCell<T> implements Callback<TableColumn<OfferModel, T>, TableCell<OfferModel, T>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TableCell<OfferModel, T> call(TableColumn<OfferModel, T> param) {
|
||||||
|
return new TableCell<OfferModel, T>(){
|
||||||
|
private OfferModel o;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateItem(T value, boolean empty) {
|
||||||
|
super.updateItem(value, empty);
|
||||||
|
if (!empty){
|
||||||
|
OfferModel offer = (OfferModel) getTableRow().getItem();
|
||||||
|
if (o != offer){
|
||||||
|
textProperty().unbind();
|
||||||
|
textProperty().bind(ModelBindings.asString(offer));
|
||||||
|
o = offer;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
textProperty().unbind();
|
||||||
|
o = null;
|
||||||
|
setText(null);
|
||||||
|
setGraphic(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package ru.trader.view.support.cells;
|
||||||
|
|
||||||
|
import javafx.scene.control.TableCell;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.scene.text.Text;
|
||||||
|
import javafx.scene.text.TextFlow;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
import javafx.util.converter.DoubleStringConverter;
|
||||||
|
import ru.trader.model.OfferDescModel;
|
||||||
|
|
||||||
|
|
||||||
|
public class PriceCellImpl implements Callback<TableColumn<OfferDescModel, Double>, TableCell<OfferDescModel, Double>> {
|
||||||
|
private final static String CSS_BAD = "bad";
|
||||||
|
private final static String CSS_GOOD = "good";
|
||||||
|
private final static String CSS_DIFF = "diff";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TableCell<OfferDescModel, Double> call(TableColumn<OfferDescModel, Double> param) {
|
||||||
|
return new PriceCell();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PriceCell extends EditingCell<OfferDescModel, Double> {
|
||||||
|
protected PriceCell() {
|
||||||
|
super(new DoubleStringConverter());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void outText() {
|
||||||
|
OfferDescModel offerDesc = (OfferDescModel) getTableRow().getItem();
|
||||||
|
if (offerDesc!=null){
|
||||||
|
double d = offerDesc.getDiff();
|
||||||
|
TextFlow txt = new TextFlow();
|
||||||
|
Text price = new Text(String.format("%.0f", offerDesc.getPrice()));
|
||||||
|
Text diff = new Text(String.format(" (%+.0f)", d));
|
||||||
|
diff.getStyleClass().add(CSS_DIFF);
|
||||||
|
txt.getChildren().addAll(price, diff);
|
||||||
|
this.getStyleClass().removeAll(CSS_BAD, CSS_GOOD);
|
||||||
|
String cssClass = (d == 0 || Double.isNaN(d) ? "" : d * offerDesc.getOffer().getType().getOrder() > 0 ? CSS_BAD : CSS_GOOD );
|
||||||
|
this.getStyleClass().add(cssClass);
|
||||||
|
this.setText(null);
|
||||||
|
this.setGraphic(txt);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package ru.trader.view.support.cells;
|
||||||
|
|
||||||
|
import com.sun.javafx.property.PropertyReference;
|
||||||
|
import javafx.beans.NamedArg;
|
||||||
|
import javafx.beans.property.ReadOnlyObjectWrapper;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.scene.control.TableColumn;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
import ru.trader.view.support.PropertyFactory;
|
||||||
|
|
||||||
|
public abstract class PropertyCellValueFactory<S,T,V> extends PropertyFactory<S, T> implements Callback<TableColumn.CellDataFeatures<T, V>, ObservableValue<V>> {
|
||||||
|
|
||||||
|
public PropertyCellValueFactory(@NamedArg("property") String property) {
|
||||||
|
super(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ObservableValue<V> call(TableColumn.CellDataFeatures<T, V> param) {
|
||||||
|
return getCellValue(param.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract ObservableValue<V> format(ObservableValue<S> value);
|
||||||
|
|
||||||
|
private ObservableValue<V> getCellValue(T rowData){
|
||||||
|
ObservableValue<S> value = null;
|
||||||
|
PropertyReference<S> prop = getPropertyRef(rowData);
|
||||||
|
if (prop!=null){
|
||||||
|
if (prop.hasProperty()) value = prop.getProperty(rowData);
|
||||||
|
else value = new ReadOnlyObjectWrapper<>(prop.get(rowData));
|
||||||
|
}
|
||||||
|
if (value == null) return null;
|
||||||
|
return format(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
package ru.trader.view.support.cells;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.scene.control.*;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.input.MouseButton;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
import javafx.util.StringConverter;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
|
public class TextFieldCell<S,T> extends TableCell<S,T> {
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(TextFieldCell.class);
|
||||||
|
|
||||||
|
private TextField textField;
|
||||||
|
private final StringConverter<T> converter;
|
||||||
|
|
||||||
|
|
||||||
|
public TextFieldCell(StringConverter<T> converter) {
|
||||||
|
this.converter = converter;
|
||||||
|
|
||||||
|
this.setOnMouseClicked((e) -> {
|
||||||
|
if (e.getButton() == MouseButton.PRIMARY)
|
||||||
|
if (!isEditing())
|
||||||
|
getTableView().edit(getTableRow().getIndex(), getTableColumn());
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static <S,T> Callback<TableColumn<S,T>, TableCell<S,T>> forTableColumn(final StringConverter<T> converter) {
|
||||||
|
return list -> new TextFieldCell<>(converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startEdit() {
|
||||||
|
LOG.trace("Start edit");
|
||||||
|
if (! isEditable()) return;
|
||||||
|
super.startEdit();
|
||||||
|
if (isEditing()){
|
||||||
|
if (textField == null) {
|
||||||
|
createTextField();
|
||||||
|
} else {
|
||||||
|
textField.setText(getItemText());
|
||||||
|
}
|
||||||
|
setText(null);
|
||||||
|
setGraphic(textField);
|
||||||
|
textField.selectAll();
|
||||||
|
textField.requestFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateItem(T item, boolean empty) {
|
||||||
|
LOG.trace("Update edit");
|
||||||
|
super.updateItem(item, empty);
|
||||||
|
if (empty) {
|
||||||
|
setText(null);
|
||||||
|
setGraphic(null);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (isEditing()) {
|
||||||
|
if (textField != null) {
|
||||||
|
textField.setText(getItemText());
|
||||||
|
}
|
||||||
|
setText(null);
|
||||||
|
setGraphic(textField);
|
||||||
|
} else {
|
||||||
|
outItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cancelEdit() {
|
||||||
|
LOG.trace("Cancel edit");
|
||||||
|
if (!isCommit()) commit(false);
|
||||||
|
if (isCommit()) {
|
||||||
|
super.cancelEdit();
|
||||||
|
outItem();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public TextField getTextField(){
|
||||||
|
return this.textField;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createTextField(){
|
||||||
|
this.textField = new TextField(getItemText());
|
||||||
|
this.setGraphic(textField);
|
||||||
|
textField.prefWidthProperty().bind(this.getTableColumn().widthProperty());
|
||||||
|
textField.setOnKeyPressed(t -> {
|
||||||
|
if (t.getCode() == KeyCode.ENTER) {
|
||||||
|
if (commit(true)) editNext();
|
||||||
|
} else if (t.getCode() == KeyCode.ESCAPE) {
|
||||||
|
textField = null;
|
||||||
|
cancelEdit();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private String getItemText(){
|
||||||
|
return converter.toString(getItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean commit(boolean noSkip) {
|
||||||
|
if (isCommit()) return true;
|
||||||
|
LOG.trace("Commit text {}", textField.getText());
|
||||||
|
try {
|
||||||
|
commitEdit(converter.fromString(textField.getText()));
|
||||||
|
} catch (NumberFormatException e){
|
||||||
|
if (noSkip) {
|
||||||
|
Platform.runLater(textField::requestFocus);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
textField = null;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void outItem(){
|
||||||
|
setText(getItemText());
|
||||||
|
setGraphic(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isCommit(){
|
||||||
|
return textField == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void editNext(){
|
||||||
|
TableView.TableViewSelectionModel sm = getTableView().getSelectionModel();
|
||||||
|
sm.selectNext();
|
||||||
|
ObservableList<TablePosition<S,T>> pos = sm.getSelectedCells();
|
||||||
|
for (TablePosition<S,T> p : pos) {
|
||||||
|
if (p.getTableColumn().isEditable()) {
|
||||||
|
getTableView().scrollTo(p.getRow()>0? p.getRow()-1 : 0);
|
||||||
|
getTableView().edit(p.getRow(), p.getTableColumn());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
21
client/src/main/resources/view/itemDesc.fxml
Normal file
21
client/src/main/resources/view/itemDesc.fxml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<?import ru.trader.view.support.cells.OfferListCell?>
|
||||||
|
<VBox xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="ru.trader.controllers.ItemDescController">
|
||||||
|
<Label text="Продавцы:"/>
|
||||||
|
<ListView fx:id="seller" maxHeight="200.0">
|
||||||
|
<cellFactory>
|
||||||
|
<OfferListCell/>
|
||||||
|
</cellFactory>
|
||||||
|
</ListView>
|
||||||
|
<Label text="Покупатели:"/>
|
||||||
|
<ListView fx:id="buyer" maxHeight="200.0">
|
||||||
|
<cellFactory>
|
||||||
|
<OfferListCell/>
|
||||||
|
</cellFactory>
|
||||||
|
</ListView>
|
||||||
|
</VBox>
|
||||||
91
client/src/main/resources/view/items.fxml
Normal file
91
client/src/main/resources/view/items.fxml
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.cell.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
|
||||||
|
|
||||||
|
<?import ru.trader.view.support.cells.OfferCellValueImpl?>
|
||||||
|
<?import ru.trader.view.support.cells.DoubleCell?>
|
||||||
|
|
||||||
|
<VBox xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="ru.trader.controllers.ItemsController">
|
||||||
|
<TableView fx:id="tblItems" editable="true" VBox.vgrow="ALWAYS">
|
||||||
|
<columns>
|
||||||
|
<TableColumn editable="true" minWidth="200.0" prefWidth="200.0" text="Товар">
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="name"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn editable="true" resizable="false" text="Покупка">
|
||||||
|
<columns>
|
||||||
|
<TableColumn minWidth="120.0" prefWidth="120.0" text="Мин.">
|
||||||
|
<cellValueFactory>
|
||||||
|
<OfferCellValueImpl property="minSell"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="80.0" prefWidth="80.0" text="Ср.">
|
||||||
|
<cellFactory>
|
||||||
|
<DoubleCell/>
|
||||||
|
</cellFactory>
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="avgSell"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="120.0" prefWidth="120.0" text="Макс.">
|
||||||
|
<cellValueFactory>
|
||||||
|
<OfferCellValueImpl property="maxSell"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
</columns>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn editable="true" resizable="false" text="Продажа">
|
||||||
|
<columns>
|
||||||
|
<TableColumn minWidth="120.0" prefWidth="120.0" text="Мин.">
|
||||||
|
<cellValueFactory>
|
||||||
|
<OfferCellValueImpl property="minBuy"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="80.0" prefWidth="80.0" text="Ср.">
|
||||||
|
<cellFactory>
|
||||||
|
<DoubleCell/>
|
||||||
|
</cellFactory>
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="avgBuy"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="120.0" prefWidth="120.0" text="Макс.">
|
||||||
|
<cellValueFactory>
|
||||||
|
<OfferCellValueImpl property="maxBuy"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
</columns>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn editable="true" resizable="false" text="Прибыль">
|
||||||
|
<columns>
|
||||||
|
<TableColumn fx:id="minProfit" minWidth="80.0" prefWidth="80.0" text="Мин.">
|
||||||
|
<cellFactory>
|
||||||
|
<DoubleCell/>
|
||||||
|
</cellFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn fx:id="avgProfit" minWidth="80.0" prefWidth="80.0" text="Ср.">
|
||||||
|
<cellFactory>
|
||||||
|
<DoubleCell/>
|
||||||
|
</cellFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn fx:id="maxProfit" minWidth="80.0" prefWidth="80.0" text="Макс." sortType="DESCENDING">
|
||||||
|
<cellFactory>
|
||||||
|
<DoubleCell/>
|
||||||
|
</cellFactory>
|
||||||
|
</TableColumn>
|
||||||
|
</columns>
|
||||||
|
</TableColumn>
|
||||||
|
</columns>
|
||||||
|
<columnResizePolicy>
|
||||||
|
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
|
||||||
|
</columnResizePolicy>
|
||||||
|
<sortOrder>
|
||||||
|
<fx:reference source="maxProfit"/>
|
||||||
|
</sortOrder>
|
||||||
|
</TableView>
|
||||||
|
</VBox>
|
||||||
37
client/src/main/resources/view/main.fxml
Normal file
37
client/src/main/resources/view/main.fxml
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<BorderPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="ru.trader.controllers.MainController"
|
||||||
|
fx:id="mainPane"
|
||||||
|
>
|
||||||
|
<top>
|
||||||
|
<MenuBar>
|
||||||
|
<Menu text="Файл">
|
||||||
|
<MenuItem text="Сохранить" onAction="#save"/>
|
||||||
|
<MenuItem text="Импорт..." onAction="#importWorld"/>
|
||||||
|
</Menu>
|
||||||
|
<Menu text="Правка">
|
||||||
|
<MenuItem text="Добавить станцию" onAction="#addVendor"/>
|
||||||
|
<MenuItem text="Добавить товар" onAction="#addItem"/>
|
||||||
|
<MenuItem text="Редактировать станцию" onAction="#editVendor"/>
|
||||||
|
</Menu>
|
||||||
|
</MenuBar>
|
||||||
|
</top>
|
||||||
|
<center>
|
||||||
|
<TabPane tabClosingPolicy="UNAVAILABLE" BorderPane.alignment="CENTER">
|
||||||
|
<Tab text="Товары">
|
||||||
|
<fx:include fx:id="items" source="items.fxml"/>
|
||||||
|
</Tab>
|
||||||
|
<Tab text="Станции">
|
||||||
|
<fx:include fx:id="offers" source="offers.fxml"/>
|
||||||
|
</Tab>
|
||||||
|
<Tab text="Маршруты">
|
||||||
|
<fx:include source="routers.fxml"/>
|
||||||
|
</Tab>
|
||||||
|
</TabPane>
|
||||||
|
</center>
|
||||||
|
|
||||||
|
</BorderPane>
|
||||||
16
client/src/main/resources/view/oEditor.fxml
Normal file
16
client/src/main/resources/view/oEditor.fxml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<?import ru.trader.view.support.NumberField?>
|
||||||
|
<VBox xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="ru.trader.controllers.OffersEditorController">
|
||||||
|
<Label fx:id="name"/>
|
||||||
|
<HBox>
|
||||||
|
<Label text="Продажа:"/>
|
||||||
|
<NumberField fx:id="sell"/>
|
||||||
|
<Label text="Покупка:"/>
|
||||||
|
<NumberField fx:id="buy"/>
|
||||||
|
</HBox>
|
||||||
|
</VBox>
|
||||||
159
client/src/main/resources/view/offers.fxml
Normal file
159
client/src/main/resources/view/offers.fxml
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.cell.PropertyValueFactory?>
|
||||||
|
<?import ru.trader.view.support.cells.PriceCellImpl?>
|
||||||
|
<?import ru.trader.view.support.cells.OfferCellValueImpl?>
|
||||||
|
|
||||||
|
<?import ru.trader.view.support.cells.DoubleCell?>
|
||||||
|
<GridPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="ru.trader.controllers.OffersController">
|
||||||
|
<columnConstraints>
|
||||||
|
<ColumnConstraints hgrow="SOMETIMES" prefWidth="100.0"/>
|
||||||
|
</columnConstraints>
|
||||||
|
<rowConstraints>
|
||||||
|
<RowConstraints fillHeight="false" maxHeight="40.0" valignment="CENTER" vgrow="SOMETIMES"/>
|
||||||
|
<RowConstraints valignment="TOP" vgrow="SOMETIMES"/>
|
||||||
|
</rowConstraints>
|
||||||
|
<HBox alignment="TOP_CENTER" fillHeight="false" spacing="10.0">
|
||||||
|
<GridPane.margin>
|
||||||
|
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0"/>
|
||||||
|
</GridPane.margin>
|
||||||
|
<Label text="Продавец:"/>
|
||||||
|
<ComboBox fx:id="vendors" prefHeight="25.0" prefWidth="222.0"/>
|
||||||
|
</HBox>
|
||||||
|
<Accordion fx:id="list" GridPane.rowIndex="1">
|
||||||
|
<panes>
|
||||||
|
<TitledPane fx:id="sells" animated="false" text="Продаваемые товары">
|
||||||
|
<TableView fx:id="tblSell" editable="true">
|
||||||
|
<columns>
|
||||||
|
<TableColumn minWidth="200.0" prefWidth="200.0" text="Товар">
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="name"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="100.0" prefWidth="100.0" resizable="false" text="Цена"
|
||||||
|
onEditCommit="#editPrice">
|
||||||
|
<cellFactory>
|
||||||
|
<PriceCellImpl/>
|
||||||
|
</cellFactory>
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="price"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn fx:id="sortSell" minWidth="80.0" prefWidth="80.0" resizable="false"
|
||||||
|
text="Прибыль" sortType="DESCENDING">
|
||||||
|
<cellFactory>
|
||||||
|
<DoubleCell/>
|
||||||
|
</cellFactory>
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="profit"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="80.0" prefWidth="80.0" resizable="false" text="AVG SELL">
|
||||||
|
<cellFactory>
|
||||||
|
<DoubleCell/>
|
||||||
|
</cellFactory>
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="avgSell"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="80.0" prefWidth="80.0" resizable="false" text="AVG BUY">
|
||||||
|
<cellFactory>
|
||||||
|
<DoubleCell/>
|
||||||
|
</cellFactory>
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="avgBuy"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="120.0" prefWidth="120.0" text="BEST SELL">
|
||||||
|
<cellValueFactory>
|
||||||
|
<OfferCellValueImpl property="bestSell"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="120.0" prefWidth="120.0" text="BEST BUY">
|
||||||
|
<cellValueFactory>
|
||||||
|
<OfferCellValueImpl property="bestBuy"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
</columns>
|
||||||
|
<columnResizePolicy>
|
||||||
|
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
|
||||||
|
</columnResizePolicy>
|
||||||
|
<sortOrder>
|
||||||
|
<fx:reference source="sortSell"/>
|
||||||
|
</sortOrder>
|
||||||
|
</TableView>
|
||||||
|
</TitledPane>
|
||||||
|
<TitledPane animated="false" layoutX="10.0" layoutY="10.0" text="Покупаемые товары">
|
||||||
|
<content>
|
||||||
|
<TableView fx:id="tblBuy" editable="true">
|
||||||
|
<columns>
|
||||||
|
<TableColumn minWidth="200.0" prefWidth="200.0" text="Товар">
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="name"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="100.0" prefWidth="100.0" resizable="false" text="Цена"
|
||||||
|
editable="true" onEditCommit="#editPrice">
|
||||||
|
<cellFactory>
|
||||||
|
<PriceCellImpl/>
|
||||||
|
</cellFactory>
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="price"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn fx:id="sortBuy" minWidth="80.0" prefWidth="80.0" resizable="false"
|
||||||
|
text="Прибыль" sortType="DESCENDING">
|
||||||
|
<cellFactory>
|
||||||
|
<DoubleCell/>
|
||||||
|
</cellFactory>
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="profit"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="80.0" prefWidth="80.0" resizable="false" text="AVG SELL">
|
||||||
|
<cellFactory>
|
||||||
|
<DoubleCell/>
|
||||||
|
</cellFactory>
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="avgSell"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="80.0" prefWidth="80.0" resizable="false" text="AVG BUY">
|
||||||
|
<cellFactory>
|
||||||
|
<DoubleCell/>
|
||||||
|
</cellFactory>
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="avgBuy"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="120.0" prefWidth="120.0" text="BEST SELL">
|
||||||
|
<cellValueFactory>
|
||||||
|
<OfferCellValueImpl property="bestSell"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="120.0" prefWidth="120.0" text="BEST BUY">
|
||||||
|
<cellValueFactory>
|
||||||
|
<OfferCellValueImpl property="bestBuy"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
</columns>
|
||||||
|
<columnResizePolicy>
|
||||||
|
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
|
||||||
|
</columnResizePolicy>
|
||||||
|
<sortOrder>
|
||||||
|
<fx:reference source="sortBuy"/>
|
||||||
|
</sortOrder>
|
||||||
|
</TableView>
|
||||||
|
</content>
|
||||||
|
</TitledPane>
|
||||||
|
</panes>
|
||||||
|
<expandedPane>
|
||||||
|
<fx:reference source="sells"/>
|
||||||
|
</expandedPane>
|
||||||
|
</Accordion>
|
||||||
|
</GridPane>
|
||||||
100
client/src/main/resources/view/orders.fxml
Normal file
100
client/src/main/resources/view/orders.fxml
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.cell.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
|
||||||
|
|
||||||
|
<?import ru.trader.view.support.cells.DoubleCell?>
|
||||||
|
|
||||||
|
<?import ru.trader.view.support.cells.OfferCellValueImpl?>
|
||||||
|
<?import ru.trader.view.support.cells.OfferTableCell?>
|
||||||
|
<HBox xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="ru.trader.controllers.OrdersController">
|
||||||
|
<TableView fx:id="tblOrders" editable="true">
|
||||||
|
<columns>
|
||||||
|
<TableColumn minWidth="200.0" prefWidth="200.0" text="Товар">
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="name"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="80.0" prefWidth="80.0" text="Цена">
|
||||||
|
<cellFactory>
|
||||||
|
<DoubleCell/>
|
||||||
|
</cellFactory>
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="price"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn fx:id="count" minWidth="80.0" prefWidth="80.0" text="Кол-во">
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="count"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="120.0" prefWidth="120.0" text="Покупатель">
|
||||||
|
<cellValueFactory>
|
||||||
|
<OfferCellValueImpl property="buyer"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn minWidth="80.0" prefWidth="80.0" text="Прибыль">
|
||||||
|
<cellFactory>
|
||||||
|
<DoubleCell/>
|
||||||
|
</cellFactory>
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="profit"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
|
||||||
|
<TableColumn resizable="false" text="Максимум">
|
||||||
|
<columns>
|
||||||
|
<TableColumn fx:id="maxCount" minWidth="80.0" prefWidth="80.0" text="Кол-во">
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="max"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn fx:id="bestProfit" minWidth="80.0" prefWidth="80.0" text="Прибыль"
|
||||||
|
sortType="DESCENDING">
|
||||||
|
<cellFactory>
|
||||||
|
<DoubleCell/>
|
||||||
|
</cellFactory>
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="bestProfit"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
</columns>
|
||||||
|
</TableColumn>
|
||||||
|
</columns>
|
||||||
|
<columnResizePolicy>
|
||||||
|
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
|
||||||
|
</columnResizePolicy>
|
||||||
|
<sortOrder>
|
||||||
|
<fx:reference source="bestProfit"/>
|
||||||
|
</sortOrder>
|
||||||
|
</TableView>
|
||||||
|
<TableView fx:id="tblBuyers">
|
||||||
|
<columns>
|
||||||
|
<TableColumn minWidth="120.0" prefWidth="120.0" text="Покупатель">
|
||||||
|
<cellFactory>
|
||||||
|
<OfferTableCell/>
|
||||||
|
</cellFactory>
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="price"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn fx:id="curProfit" minWidth="80.0" prefWidth="80.0" text="Прибыль" sortType="DESCENDING">
|
||||||
|
<cellFactory>
|
||||||
|
<DoubleCell/>
|
||||||
|
</cellFactory>
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="profit"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
</columns>
|
||||||
|
<columnResizePolicy>
|
||||||
|
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
|
||||||
|
</columnResizePolicy>
|
||||||
|
<sortOrder>
|
||||||
|
<fx:reference source="curProfit"/>
|
||||||
|
</sortOrder>
|
||||||
|
</TableView>
|
||||||
|
</HBox>
|
||||||
23
client/src/main/resources/view/routers.fxml
Normal file
23
client/src/main/resources/view/routers.fxml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<?import ru.trader.view.support.NumberField?>
|
||||||
|
<VBox xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"
|
||||||
|
fx:controller="ru.trader.controllers.RoutersController">
|
||||||
|
<HBox>
|
||||||
|
<HBox>
|
||||||
|
<Label text="Баланс:"/>
|
||||||
|
<NumberField fx:id="balance" value="1000"/>
|
||||||
|
</HBox>
|
||||||
|
<HBox>
|
||||||
|
<Label text="Трюм:"/>
|
||||||
|
<NumberField fx:id="cargo" value="4"/>
|
||||||
|
</HBox>
|
||||||
|
<Label text="Станция:"/>
|
||||||
|
<ComboBox fx:id="vendors"/>
|
||||||
|
<Button fx:id="buy" text="Купить"/>
|
||||||
|
<Button fx:id="sell" text="Продать"/>
|
||||||
|
</HBox>
|
||||||
|
</VBox>
|
||||||
11
client/src/main/resources/view/style.css
Normal file
11
client/src/main/resources/view/style.css
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
.good .diff {
|
||||||
|
-fx-fill: green;
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bad .diff {
|
||||||
|
-fx-fill: red;
|
||||||
|
-fx-font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
32
client/src/main/resources/view/vEditor.fxml
Normal file
32
client/src/main/resources/view/vEditor.fxml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.*?>
|
||||||
|
<?import javafx.scene.layout.*?>
|
||||||
|
|
||||||
|
<?import javafx.scene.control.cell.PropertyValueFactory?>
|
||||||
|
|
||||||
|
<VBox xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8"
|
||||||
|
fx:controller="ru.trader.controllers.VendorEditorController">
|
||||||
|
<HBox alignment="CENTER" GridPane.columnSpan="3">
|
||||||
|
<TextField fx:id="name" alignment="CENTER"/>
|
||||||
|
</HBox>
|
||||||
|
<TableView fx:id="items" editable="true" prefWidth="400.0">
|
||||||
|
<columns>
|
||||||
|
<TableColumn minWidth="200.0" prefWidth="200.0" text="Товар" editable="false">
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="name"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn fx:id="buy" minWidth="80.0" prefWidth="80.0" maxWidth="80.0" text="Продажа">
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="bprice"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
<TableColumn fx:id="sell" minWidth="80.0" prefWidth="80.0" maxWidth="80.0" text="Покупка">
|
||||||
|
<cellValueFactory>
|
||||||
|
<PropertyValueFactory property="sprice"/>
|
||||||
|
</cellValueFactory>
|
||||||
|
</TableColumn>
|
||||||
|
</columns>
|
||||||
|
</TableView>
|
||||||
|
</VBox>
|
||||||
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>
|
||||||
114
pom.xml
Normal file
114
pom.xml
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
<?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>
|
||||||
|
|
||||||
|
<groupId>trader</groupId>
|
||||||
|
<artifactId>Trader</artifactId>
|
||||||
|
<version>1.0</version>
|
||||||
|
|
||||||
|
<organization>
|
||||||
|
<name>Open Software</name>
|
||||||
|
</organization>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<jdk.version>1.8</jdk.version>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>core</module>
|
||||||
|
<module>utils</module>
|
||||||
|
<module>client</module>
|
||||||
|
</modules>
|
||||||
|
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
<version>1.7.7</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi</artifactId>
|
||||||
|
<version>3.10-FINAL</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi-ooxml</artifactId>
|
||||||
|
<version>3.10-FINAL</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi-ooxml-schemas</artifactId>
|
||||||
|
<version>3.10-FINAL</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-log4j12</artifactId>
|
||||||
|
<version>1.7.7</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>log4j</groupId>
|
||||||
|
<artifactId>log4j</artifactId>
|
||||||
|
<version>1.2.17</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.11</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jetbrains</groupId>
|
||||||
|
<artifactId>annotations</artifactId>
|
||||||
|
<version>13.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.controlsfx</groupId>
|
||||||
|
<artifactId>controlsfx</artifactId>
|
||||||
|
<version>8.0.6</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<pluginManagement>
|
||||||
|
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>1.8</source>
|
||||||
|
<target>1.8</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.zenjava</groupId>
|
||||||
|
<artifactId>javafx-maven-plugin</artifactId>
|
||||||
|
<version>2.0</version>
|
||||||
|
<configuration>
|
||||||
|
<mainClass>ru.trader.Main</mainClass>
|
||||||
|
</configuration>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.twdata.maven</groupId>
|
||||||
|
<artifactId>mojo-executor</artifactId>
|
||||||
|
<version>2.1.0</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
|
||||||
|
</project>
|
||||||
50
utils/pom.xml
Normal file
50
utils/pom.xml
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
<?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>utils</artifactId>
|
||||||
|
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi-ooxml</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi-ooxml-schemas</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<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>
|
||||||
4
utils/src/main/java/META-INF/MANIFEST.MF
Normal file
4
utils/src/main/java/META-INF/MANIFEST.MF
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Manifest-Version: 1.0
|
||||||
|
Class-Path: slf4j-api-1.7.5.jar poi-3.10-FINAL-20140208.jar poi-ooxml-
|
||||||
|
3.10-FINAL-20140208.jar poi-ooxml-schemas-3.10-FINAL-20140208.jar
|
||||||
|
|
||||||
149
utils/src/main/java/ru/trader/store/XSSFImporter.java
Normal file
149
utils/src/main/java/ru/trader/store/XSSFImporter.java
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
package ru.trader.store;
|
||||||
|
|
||||||
|
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
||||||
|
import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
|
||||||
|
import org.apache.poi.openxml4j.opc.OPCPackage;
|
||||||
|
import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable;
|
||||||
|
import org.apache.poi.xssf.eventusermodel.XSSFReader;
|
||||||
|
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
|
||||||
|
import org.apache.poi.xssf.model.StylesTable;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.xml.sax.ContentHandler;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
import org.xml.sax.XMLReader;
|
||||||
|
import org.xml.sax.helpers.XMLReaderFactory;
|
||||||
|
import ru.trader.core.*;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class XSSFImporter {
|
||||||
|
private final static Logger LOG = LoggerFactory.getLogger(XSSFImporter.class);
|
||||||
|
|
||||||
|
private OPCPackage pkg;
|
||||||
|
private final XSSFReader reader;
|
||||||
|
protected final Iterator<InputStream> sheetItr;
|
||||||
|
protected final Market market;
|
||||||
|
protected final List<Vendor> vendors;
|
||||||
|
|
||||||
|
public XSSFImporter(File file) throws IOException {
|
||||||
|
pkg = null;
|
||||||
|
try {
|
||||||
|
pkg = OPCPackage.open(file.getPath());
|
||||||
|
reader = new XSSFReader(pkg);
|
||||||
|
sheetItr = reader.getSheetsData();
|
||||||
|
market = new SimpleMarket();
|
||||||
|
vendors = new ArrayList<>();
|
||||||
|
} catch (OpenXML4JException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Market doImport() throws IOException, SAXException {
|
||||||
|
if (pkg==null) throw new IllegalStateException("No init package");
|
||||||
|
try {
|
||||||
|
XMLReader saxParser = XMLReaderFactory.createXMLReader();
|
||||||
|
saxParser.setContentHandler(getHandler());
|
||||||
|
saxParser.parse(getInputSource());
|
||||||
|
market.addVendors(vendors);
|
||||||
|
} finally {
|
||||||
|
if (pkg!=null) {
|
||||||
|
try { pkg.close();} catch (IOException e) {LOG.warn("Error on close pkg",e);}
|
||||||
|
pkg = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return market;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected InputSource getInputSource() {
|
||||||
|
return new InputSource(sheetItr.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ContentHandler getHandler() throws IOException, SAXException {
|
||||||
|
StylesTable styles;
|
||||||
|
try {
|
||||||
|
styles = reader.getStylesTable();
|
||||||
|
} catch (InvalidFormatException e) {
|
||||||
|
throw new SAXException(e);
|
||||||
|
}
|
||||||
|
ReadOnlySharedStringsTable sharedStrings = new ReadOnlySharedStringsTable(pkg);
|
||||||
|
return new XSSFSheetXMLHandler(styles, sharedStrings, new LoaderSheetContentsHandler(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected class LoaderSheetContentsHandler implements XSSFSheetXMLHandler.SheetContentsHandler {
|
||||||
|
private Item item;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startRow(int rowNum) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endRow() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cell(String cellReference, String formattedValue) {
|
||||||
|
LOG.trace("Parce cell {}, value = {}", cellReference, formattedValue);
|
||||||
|
if (formattedValue==null||formattedValue.isEmpty()) return;
|
||||||
|
RC cell = new RC(cellReference);
|
||||||
|
if (cell.row == 1) {
|
||||||
|
if (cell.column > 1 &&cell.column % 2 == 0){
|
||||||
|
LOG.trace("add vendor");
|
||||||
|
vendors.add(new SimpleVendor(formattedValue));
|
||||||
|
}
|
||||||
|
} else if (cell.row>2){
|
||||||
|
if (cell.column == 1){
|
||||||
|
LOG.trace("create item");
|
||||||
|
item = new Item(formattedValue);
|
||||||
|
market.add(item);
|
||||||
|
} else {
|
||||||
|
LOG.trace("add offer");
|
||||||
|
Offer offer = new Offer(cell.column % 2 == 0? OFFER_TYPE.BUY : OFFER_TYPE.SELL, item, Double.valueOf(formattedValue));
|
||||||
|
vendors.get(cell.column/2 -1).add(offer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void headerFooter(String text, boolean isHeader, String tagName) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static final Pattern CELL_REF = Pattern.compile("(\\D+)(\\d+)");
|
||||||
|
private class RC {
|
||||||
|
int row;
|
||||||
|
int column;
|
||||||
|
|
||||||
|
public RC(String cellReference){
|
||||||
|
Matcher matcher = CELL_REF.matcher(cellReference);
|
||||||
|
if (matcher.find()){
|
||||||
|
column = wordToColumn(matcher.group(1));
|
||||||
|
row = Integer.valueOf(matcher.group(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int wordToColumn(String word){
|
||||||
|
word = word.toUpperCase();
|
||||||
|
int column = -1;
|
||||||
|
for (int i = 0; i < word.length(); ++i) {
|
||||||
|
int c = word.charAt(i);
|
||||||
|
column = (column + 1) * 26 + c - 'A';
|
||||||
|
}
|
||||||
|
return column+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
27
utils/utils.iml
Normal file
27
utils/utils.iml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?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" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="module" module-name="core" />
|
||||||
|
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.7" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.jetbrains:annotations:13.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.apache.poi:poi:3.10-FINAL" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: commons-codec:commons-codec:1.5" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.apache.poi:poi-ooxml:3.10-FINAL" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.apache.poi:poi-ooxml-schemas:3.10-FINAL" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: org.apache.xmlbeans:xmlbeans:2.3.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: stax:stax-api:1.0.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: dom4j:dom4j:1.6.1" level="project" />
|
||||||
|
<orderEntry type="library" name="Maven: xml-apis:xml-apis:1.0.b2" level="project" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
|
|
||||||
Reference in New Issue
Block a user