From 7c1e828eaea6eab3349baf20915d50902c93f416 Mon Sep 17 00:00:00 2001 From: iMoHax Date: Wed, 21 Jan 2015 13:20:43 +0300 Subject: [PATCH] implement search stations --- .../trader/controllers/SearchController.java | 199 ++++++++++++++++++ .../main/java/ru/trader/model/ItemModel.java | 8 +- .../java/ru/trader/model/MarketModel.java | 9 + .../java/ru/trader/model/ModelFabric.java | 113 +++++++++- .../resources/lang/locale_en_US.properties | 12 +- .../resources/lang/locale_ru_RU.properties | 12 +- client/src/main/resources/view/main.fxml | 5 +- client/src/main/resources/view/router.fxml | 2 +- client/src/main/resources/view/search.fxml | 79 +++++++ core/src/main/java/ru/trader/core/Market.java | 5 +- .../java/ru/trader/core/MarketAnalyzer.java | 60 +++++- .../java/ru/trader/core/PlacesWrapper.java | 8 +- .../java/ru/trader/core/VendorsIterator.java | 9 +- .../java/ru/trader/graph/RouteGraphTest.java | 4 +- .../ru/trader/graph/RouteSearcherTest.java | 12 +- .../ru/trader/store/berkeley/MarketTest.java | 7 +- 16 files changed, 507 insertions(+), 37 deletions(-) create mode 100644 client/src/main/java/ru/trader/controllers/SearchController.java create mode 100644 client/src/main/resources/view/search.fxml diff --git a/client/src/main/java/ru/trader/controllers/SearchController.java b/client/src/main/java/ru/trader/controllers/SearchController.java new file mode 100644 index 0000000..bd5b548 --- /dev/null +++ b/client/src/main/java/ru/trader/controllers/SearchController.java @@ -0,0 +1,199 @@ +package ru.trader.controllers; + +import javafx.beans.property.*; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.scene.control.*; +import javafx.util.StringConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import ru.trader.core.MarketFilter; +import ru.trader.core.SERVICE_TYPE; +import ru.trader.model.*; +import ru.trader.model.support.BindingsHelper; +import ru.trader.model.support.ChangeMarketListener; +import ru.trader.view.support.NumberField; +import ru.trader.view.support.cells.CustomListCell; + +import java.util.Collection; +import java.util.List; + + +public class SearchController { + private final static Logger LOG = LoggerFactory.getLogger(SearchController.class); + + @FXML + private ComboBox source; + @FXML + private ComboBox items; + @FXML + private NumberField distance; + @FXML + private CheckBox cbMarket; + @FXML + private CheckBox cbBlackMarket; + @FXML + private CheckBox cbRepair; + @FXML + private CheckBox cbMunition; + @FXML + private CheckBox cbOutfit; + @FXML + private CheckBox cbShipyard; + @FXML + private CheckBox cbMediumLandpad; + @FXML + private CheckBox cbLargeLandpad; + @FXML + private TableView tblResults; + + private final List results = FXCollections.observableArrayList(); + private final ObservableList itemsList = FXCollections.observableArrayList(); + + private MarketModel market; + + @FXML + private void initialize() { + items.setCellFactory(new CustomListCell<>(ItemModel::getName)); + items.setConverter(new StringConverter() { + @Override + public String toString(ItemModel item) { + return item.getName(); + } + + @Override + public ItemModel fromString(String string) { + throw new UnsupportedOperationException("Is not editable field"); + } + }); + BindingsHelper.setTableViewItems(tblResults, results); + items.setItems(itemsList); + init(); + } + + void init(){ + market = MainController.getMarket(); + market.getNotificator().add(new SearchChangeListener()); + source.setItems(market.systemsProperty()); + itemsList.clear(); + itemsList.add(ModelFabric.NONE_ITEM); + itemsList.addAll(market.itemsProperty().get()); + source.getSelectionModel().selectFirst(); + } + + + private void addItem(ItemModel item){ + itemsList.add(item); + } + + private void removeItem(ItemModel item){ + itemsList.remove(item); + } + + @FXML + private void searchStations(){ + MarketFilter filter = new MarketFilter(); + filter.setDistance(distance.getValue().doubleValue()); + if (cbMarket.isSelected()) filter.add(SERVICE_TYPE.MARKET); else filter.remove(SERVICE_TYPE.MARKET); + if (cbBlackMarket.isSelected()) filter.add(SERVICE_TYPE.BLACK_MARKET); else filter.remove(SERVICE_TYPE.BLACK_MARKET); + if (cbMunition.isSelected()) filter.add(SERVICE_TYPE.MUNITION); else filter.remove(SERVICE_TYPE.MUNITION); + if (cbRepair.isSelected()) filter.add(SERVICE_TYPE.REPAIR); else filter.remove(SERVICE_TYPE.REPAIR); + if (cbOutfit.isSelected()) filter.add(SERVICE_TYPE.OUTFIT); else filter.remove(SERVICE_TYPE.OUTFIT); + if (cbShipyard.isSelected()) filter.add(SERVICE_TYPE.SHIPYARD); else filter.remove(SERVICE_TYPE.SHIPYARD); + if (cbMediumLandpad.isSelected()) filter.add(SERVICE_TYPE.MEDIUM_LANDPAD); else filter.remove(SERVICE_TYPE.MEDIUM_LANDPAD); + if (cbLargeLandpad.isSelected()) filter.add(SERVICE_TYPE.LARGE_LANDPAD); else filter.remove(SERVICE_TYPE.LARGE_LANDPAD); + ItemModel item = items.getValue(); + if (item == null || item == ModelFabric.NONE_ITEM){ + Collection stations = market.getStations(filter); + fill(stations); + } else { + Collection offers = market.getOffers(item, filter); + fill(offers); + } + } + + private void fill(Collection entries){ + results.clear(); + for (Object entry : entries) { + if (entry instanceof StationModel){ + results.add(new ResultEntry((StationModel) entry)); + } else { + if (entry instanceof OfferModel) { + results.add(new ResultEntry((OfferModel) entry)); + } else { + throw new IllegalArgumentException("Argument must have StationModel or OfferModel class"); + } + } + } + } + + + + public class ResultEntry { + private final StationModel station; + private final OfferModel offer; + private final ReadOnlyDoubleProperty distance; + + private ResultEntry(StationModel station) { + this(station, null); + } + + private ResultEntry(OfferModel offer) { + this(offer.getStation(), offer); + } + + private ResultEntry(StationModel station, OfferModel offer) { + this.station = station; + this.offer = offer; + this.distance = new SimpleDoubleProperty(source.getValue().getDistance(station.getSystem())); + } + + public SystemModel getSystem(){ + return station.getSystem(); + } + + private StationModel getStation(){ + return station; + } + + private OfferModel getOffer() { + return offer; + } + + public ReadOnlyStringProperty stationProperty(){ + return new SimpleStringProperty(String.format("%s (%.0f Ls)", station.getName(), station.getDistance())); + } + + public ReadOnlyStringProperty nameProperty(){ + return offer != null ? offer.nameProperty() : new SimpleStringProperty(""); + } + + public ReadOnlyDoubleProperty priceProperty(){ + return offer != null ? offer.priceProperty() : new SimpleDoubleProperty(Double.NaN); + } + + public ReadOnlyLongProperty countProperty(){ + return offer != null ? offer.countProperty() : new SimpleLongProperty(0); + } + + public ReadOnlyDoubleProperty distanceProperty(){ + return distance; + } + + } + + private class SearchChangeListener extends ChangeMarketListener { + + @Override + public void add(ItemModel item) { + addItem(item); + } + + @Override + public void remove(ItemModel item) { + removeItem(item); + } + } + +} diff --git a/client/src/main/java/ru/trader/model/ItemModel.java b/client/src/main/java/ru/trader/model/ItemModel.java index c3bb210..c1e71c0 100644 --- a/client/src/main/java/ru/trader/model/ItemModel.java +++ b/client/src/main/java/ru/trader/model/ItemModel.java @@ -18,6 +18,12 @@ public class ItemModel { private final ItemStatModel statSell; private final ItemStatModel statBuy; + ItemModel() { + this.item = null; + this.statSell = null; + this.statBuy = null; + } + ItemModel(Item item, MarketModel market) { this.item = item; this.statSell = new ItemStatModel(market.getStat(OFFER_TYPE.SELL, item), market); @@ -30,7 +36,7 @@ public class ItemModel { public String getId() {return item.getName();} - public String getName() {return name != null ? name.get() : item.getName();} + public String getName() {return name != null ? name.get() : Localization.getString("item." + item.getName(), item.getName());} public void setName(String value) { LOG.info("Change name of item {} to {}", item, value); diff --git a/client/src/main/java/ru/trader/model/MarketModel.java b/client/src/main/java/ru/trader/model/MarketModel.java index 79e1d45..c212b54 100644 --- a/client/src/main/java/ru/trader/model/MarketModel.java +++ b/client/src/main/java/ru/trader/model/MarketModel.java @@ -20,6 +20,7 @@ import ru.trader.services.OrdersSearchTask; import ru.trader.services.RoutesSearchTask; import ru.trader.view.support.Localization; +import java.util.Collection; import java.util.function.Consumer; @@ -122,6 +123,14 @@ public class MarketModel { return market.getStat(type, item); } + public ObservableList getOffers(ItemModel item, MarketFilter filter){ + return BindingsHelper.observableList(analyzer.getOffers(item.getItem(), filter), modeler::get); + } + + public Collection getStations(MarketFilter filter){ + return BindingsHelper.observableList(analyzer.getVendors(filter), modeler::get); + } + public void getOrders(SystemModel from, double balance, Consumer> result) { getOrders(from, ModelFabric.NONE_STATION, ModelFabric.NONE_SYSTEM, ModelFabric.NONE_STATION, balance, result); } diff --git a/client/src/main/java/ru/trader/model/ModelFabric.java b/client/src/main/java/ru/trader/model/ModelFabric.java index 23f75d9..dfd8630 100644 --- a/client/src/main/java/ru/trader/model/ModelFabric.java +++ b/client/src/main/java/ru/trader/model/ModelFabric.java @@ -1,5 +1,7 @@ package ru.trader.model; +import javafx.beans.property.ReadOnlyDoubleProperty; +import javafx.beans.property.ReadOnlyObjectProperty; import javafx.beans.property.ReadOnlyStringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -117,6 +119,7 @@ public class ModelFabric { public static SystemModel NONE_SYSTEM = new FAKE_SYSTEM_MODEL(); public static StationModel NONE_STATION = new FAKE_STATION_MODEL(); + public static ItemModel NONE_ITEM = new FAKE_ITEM_MODEL(); private static class FAKE_SYSTEM_MODEL extends SystemModel { FAKE_SYSTEM_MODEL() { @@ -130,7 +133,7 @@ public class ModelFabric { @Override public String getName() { - throw new UnsupportedOperationException("Is fake system, change unsupported"); + return ""; } @Override @@ -207,7 +210,7 @@ public class ModelFabric { @Override public String getName() { - throw new UnsupportedOperationException("Is fake system, unsupported"); + return ""; } @Override @@ -290,4 +293,110 @@ public class ModelFabric { return ""; } } + + public static class FAKE_ITEM_MODEL extends ItemModel { + + FAKE_ITEM_MODEL() { + super(); + } + + FAKE_ITEM_MODEL(Item item, MarketModel market) { + super(item, market); + } + + @Override + Item getItem() { + throw new UnsupportedOperationException("Is fake item, unsupported"); + } + + @Override + public String getId() { + throw new UnsupportedOperationException("Is fake item, unsupported"); + } + + @Override + public String getName() { + return ""; + } + + @Override + public void setName(String value) { + throw new UnsupportedOperationException("Is fake item, unsupported"); + } + + @Override + public ReadOnlyStringProperty nameProperty() { + throw new UnsupportedOperationException("Is fake item, unsupported"); + } + + @Override + public ReadOnlyDoubleProperty avgBuyProperty() { + throw new UnsupportedOperationException("Is fake item, unsupported"); + } + + @Override + public ReadOnlyObjectProperty minBuyProperty() { + throw new UnsupportedOperationException("Is fake item, unsupported"); + } + + @Override + public ReadOnlyObjectProperty maxBuyProperty() { + throw new UnsupportedOperationException("Is fake item, unsupported"); + } + + @Override + public ReadOnlyObjectProperty bestBuyProperty() { + throw new UnsupportedOperationException("Is fake item, unsupported"); + } + + @Override + public ReadOnlyDoubleProperty avgSellProperty() { + throw new UnsupportedOperationException("Is fake item, unsupported"); + } + + @Override + public ReadOnlyObjectProperty minSellProperty() { + throw new UnsupportedOperationException("Is fake item, unsupported"); + } + + @Override + public ReadOnlyObjectProperty maxSellProperty() { + throw new UnsupportedOperationException("Is fake item, unsupported"); + } + + @Override + public ReadOnlyObjectProperty bestSellProperty() { + throw new UnsupportedOperationException("Is fake item, unsupported"); + } + + @Override + public List getSeller() { + throw new UnsupportedOperationException("Is fake item, unsupported"); + } + + @Override + public List getBuyer() { + throw new UnsupportedOperationException("Is fake item, unsupported"); + } + + @Override + public boolean isMarketItem() { + throw new UnsupportedOperationException("Is fake item, unsupported"); + } + + @Override + public void refresh() { + throw new UnsupportedOperationException("Is fake item, unsupported"); + } + + @Override + public void refresh(OFFER_TYPE type) { + throw new UnsupportedOperationException("Is fake item, unsupported"); + } + + @Override + public String toString() { + return ""; + } + } } diff --git a/client/src/main/resources/lang/locale_en_US.properties b/client/src/main/resources/lang/locale_en_US.properties index 272c67d..31999cb 100644 --- a/client/src/main/resources/lang/locale_en_US.properties +++ b/client/src/main/resources/lang/locale_en_US.properties @@ -29,7 +29,6 @@ market.order.seller=Seller market.order.distance=Distance # Route -routes=Routes routes.path=Path routes.jumps=Jumps routes.refills=Refills @@ -64,6 +63,8 @@ main.menu.settings.language=Language main.menu.settings.language.item=English main.menu.settings.parameters=Preferences main.menu.settings.filter=Filter +main.tab.routes=Routes +main.tab.search=Search # add item dialog dialog.item.title=Adding new commodity @@ -118,6 +119,7 @@ router.pane.route.to=To: router.pane.route.jumps=Jumps: router.button.recompute=Recompute router.button.rebuild=Rebuild +router.button.routes=Routes router.button.top=TOP 100 router.pane.total=Total router.pane.total.profit=Profit: @@ -155,4 +157,10 @@ analyzer.find.success=%d paths found analyzer.graph.station.build=Building graph from %s (%s) analyzer.graph.build=Building graph from %s analyzer.graph.success=Graph is built -analyser.finish=Finish \ No newline at end of file +analyser.finish=Finish + +# search.fxml +search.text.from=From: +search.text.item=Commodity: +search.text.distance=Distance \nto station(Ls): +search.button.find=Find \ No newline at end of file diff --git a/client/src/main/resources/lang/locale_ru_RU.properties b/client/src/main/resources/lang/locale_ru_RU.properties index 6740a50..46e1331 100644 --- a/client/src/main/resources/lang/locale_ru_RU.properties +++ b/client/src/main/resources/lang/locale_ru_RU.properties @@ -29,7 +29,6 @@ market.order.seller=\u041F\u0440\u043E\u0434\u0430\u0432\u0435\u0446 market.order.distance=\u0414\u0438\u0441\u0442\u0430\u043D\u0446\u0438\u044F # Route -routes=\u041C\u0430\u0440\u0448\u0440\u0443\u0442\u044B routes.path=\u041F\u0443\u0442\u044C routes.jumps=\u041F\u0440\u044B\u0436\u043A\u043E\u0432 routes.refills=\u0417\u0430\u043F\u0440\u0430\u0432\u043E\u043A @@ -65,6 +64,8 @@ main.menu.settings.language=\u042F\u0437\u044B\u043A main.menu.settings.language.item=\u0420\u0443\u0441\u0441\u043A\u0438\u0439 main.menu.settings.parameters=\u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B main.menu.settings.filter=\u0424\u0438\u043B\u044C\u0442\u0440 +main.tab.routes=\u041C\u0430\u0440\u0448\u0440\u0443\u0442\u044B +main.tab.search=\u041F\u043E\u0438\u0441\u043A # add item dialog dialog.item.title=\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u0435 \u043D\u043E\u0432\u043E\u0433\u043E \u0442\u043E\u0432\u0430\u0440\u0430 @@ -118,6 +119,7 @@ router.pane.route.to=\u0414\u043E: router.pane.route.jumps=\u041F\u0440\u044B\u0436\u043A\u043E\u0432: router.button.recompute=\u041F\u0435\u0440\u0435\u0441\u0447\u0438\u0442\u0430\u0442\u044C router.button.rebuild=\u041F\u0435\u0440\u0435\u0441\u0442\u0440\u043E\u0438\u0442\u044C +router.button.routes=\u041C\u0430\u0440\u0448\u0440\u0443\u0442\u044B router.button.top=TOP 100 router.pane.total=\u0418\u0442\u043E\u0433\u043E router.pane.total.profit=\u041F\u0440\u0438\u0431\u044B\u043B\u044C: @@ -155,4 +157,10 @@ analyzer.find.success=%d \u043F\u0443\u0442\u0435\u0439 \u043D\u0430\u0439\u0434 analyzer.graph.station.build=\u041F\u043E\u0441\u0442\u0440\u043E\u0439\u043A\u0430 \u0433\u0440\u0430\u0444\u0430 \u043E\u0442 %s (%s) analyzer.graph.build=\u041F\u043E\u0441\u0442\u0440\u043E\u0439\u043A\u0430 \u0433\u0440\u0430\u0444\u0430 \u043E\u0442 %s analyzer.graph.success=\u0413\u0440\u0430\u0444 \u043F\u043E\u0441\u0442\u0440\u043E\u0435\u043D -analyser.finish=\u0413\u043E\u0442\u043E\u0432\u043E \ No newline at end of file +analyser.finish=\u0413\u043E\u0442\u043E\u0432\u043E + +# search.fxml +search.text.from=\u041E\u0442: +search.text.item=\u0422\u043E\u0432\u0430\u0440: +search.text.distance=\u0414\u0438\u0441\u0442\u0430\u043D\u0446\u0438\u044F \n\u0434\u043E \u0441\u0442\u0430\u043D\u0446\u0438\u0438(Ls): +search.button.find=\u041D\u0430\u0439\u0442\u0438 \ No newline at end of file diff --git a/client/src/main/resources/view/main.fxml b/client/src/main/resources/view/main.fxml index c99fa86..6df1c88 100644 --- a/client/src/main/resources/view/main.fxml +++ b/client/src/main/resources/view/main.fxml @@ -45,9 +45,12 @@ - + + + + diff --git a/client/src/main/resources/view/router.fxml b/client/src/main/resources/view/router.fxml index 3e655f0..f970804 100644 --- a/client/src/main/resources/view/router.fxml +++ b/client/src/main/resources/view/router.fxml @@ -64,7 +64,7 @@