Copy Tour & Log + Progressbar Verbessert

This commit is contained in:
Georg Reisinger 2021-04-27 23:53:05 +02:00
parent e56e77dce0
commit 792aea12c4
10 changed files with 234 additions and 82 deletions

View File

@ -106,4 +106,7 @@ rsummrepo = Zusammengefasster Report
bewertung = Bewertung
bewertungmsg = Bitte die Bewertung zwischen 0 und 5 eingeben
bewertungeror = Bitte nur Bewertungen von 0 und 5 eingeben
bewertungerror2 = Bitte nur Zahlen zum Bewerten
bewertungerror2 = Bitte nur Zahlen zum Bewerten
copybtn = Kopieren
rating = Bewertung

View File

@ -107,4 +107,7 @@ rsummrepo = Summary Report
bewertung = Rating
bewertungmsg = Please enter a rating from 0 to 5
bewertungeror = Please enter only a rating from 0 to 5
bewertungerror2 = Please only enter numbers for rating
bewertungerror2 = Please only enter numbers for rating
copybtn = Copy
rating = Rating

View File

@ -29,9 +29,9 @@ public class Exporter {
*/
public void doExport() throws IOException {
ProgressBar progressBar = new ProgressBar("Export...");
int step = progressBar.getProgressSize(4, 100);
ArrayList<Tour> data = new DbConnect().getAllTouren();
ArrayList<Tour> data = new DbConnect().getAllTouren(progressBar, 15);
int step = progressBar.getProgressSize(4, 100);
progressBar.addProgress(step);
FileWriter fileWriter = new FileWriter(EinheitenAdder.addJson(this.path));
progressBar.addProgress(step);
@ -45,20 +45,18 @@ public class Exporter {
/**
* Führt den Imput aus: File holen, Daten in db erstellen
*/
public void doImport() {
ProgressBar progressBar = new ProgressBar("Import...");
public void doImport(ProgressBar progressBar, int progressMax) {
try {
ArrayList<Tour> touren = JsonHelper.getTourenFromJson(new FileReader(EinheitenAdder.addJson(this.path)));
DbConnect dbConnect = new DbConnect();
ArrayList<Tour> allTours = dbConnect.getAllTouren();
progressBar.addProgress(2);
int size = progressBar.getProgressSize(allTours.size(), 25);
ArrayList<Tour> allTours = dbConnect.getAllTouren(progressBar, 15);
int size = progressBar.getProgressSize(allTours.size(), 35);
allTours.forEach(t -> {
TourPlaner.delTour(t.getName());
progressBar.addProgress(size);
});
int finalSize = progressBar.getProgressSize(touren.size() * 3, 100);
int finalSize = progressBar.getProgressSize(touren.size() * 3, progressMax);
for (Tour tour: touren) {
new DirectionMap(tour.getStart(), tour.getZiel(), tour.getName());
progressBar.addProgress(finalSize);
@ -72,8 +70,8 @@ public class Exporter {
}
} catch (IOException e) {
LogHelper.error(e);
progressBar.setProgress(100);
progressBar.setProgress(progressMax);
}
progressBar.setProgress(100);
progressBar.setProgress(progressMax);
}
}

View File

@ -7,6 +7,7 @@ import tourplaner.object.Log;
import tourplaner.object.Tour;
import com.itextpdf.text.pdf.PdfWriter;
import tourplaner.ui.ProgressBar;
import java.awt.*;
import java.io.FileNotFoundException;
@ -33,8 +34,8 @@ public class Reporter {
/**
* Erstellt den Summary Report
*/
public static void sumReport(){
ArrayList<Tour> tours = TourPlaner.getAllTours();
public static void sumReport(ProgressBar progressBar, int maxLevel){
ArrayList<Tour> tours = TourPlaner.getAllTours(progressBar, maxLevel);
String file = ConfigHelper.getIniString(ConfigHelper.getStandartConfig(), "report", "path") + "summary.pdf";
try {
Document document = new Document();

View File

@ -1,5 +1,6 @@
package tourplaner.business;
import javafx.collections.ObservableList;
import org.apache.log4j.Logger;
import tourplaner.data.DbConnect;
import tourplaner.object.Log;
@ -11,6 +12,8 @@ import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Locale;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
/**
* Haupt Logik des Tourplaners
@ -28,8 +31,8 @@ public class TourPlaner{
* Holt alle touren aus der DB
* @return Alle touren als ArrayList
*/
public static ArrayList<Tour> getAllTours(){
return new DbConnect().getAllTouren();
public static ArrayList<Tour> getAllTours(ProgressBar progressBar, int maxLevel){
return new DbConnect().getAllTouren(progressBar, maxLevel);
}
/**
@ -69,20 +72,32 @@ public class TourPlaner{
* Fügt eine Tour hinzu
* @param newTour Neue Tour
* @param progressBar Aktuelle Progressbar
* @param progressMax Maximale anzeige der Progress bar
* @return false bei error
* @throws IOException Fehler beim hinzufügen der Tour
*/
public static boolean addTour(Tour newTour, ProgressBar progressBar) throws IOException {
int step = progressBar.getProgressSize(2, 100);
public static boolean addTourMax(Tour newTour, ProgressBar progressBar, int progressMax) throws IOException {
int step = progressBar.getProgressSize(2, progressMax);
DirectionMap directionMap = new DirectionMap(newTour.getStart(), newTour.getZiel(), newTour.getName());
progressBar.addProgress(step);
newTour.setDauer(directionMap.getDauer()+"");
newTour.setStrecke(directionMap.getStrecke());
boolean ret = new DbConnect().addTour(newTour);
progressBar.setProgress(100);
progressBar.setProgress(progressMax);
return ret;
}
/**
* Fügt eine Tour hinzu
* @param newTour Neue Tour
* @param progressBar Aktuelle Progressbar
* @return false bei error
* @throws IOException Fehler beim hinzufügen der Tour
*/
public static boolean addTour(Tour newTour, ProgressBar progressBar) throws IOException {
return addTourMax(newTour, progressBar, 100);
}
/**
* Path zu dem Bild für die GUI
* @param tourname Name der Tour
@ -165,9 +180,9 @@ public class TourPlaner{
* @param tourname Name der zu suchen ist
* @return Alle touren die auf den Suchterm passen
*/
public static ArrayList<Tour> sucheTour(String tourname){
public static ArrayList<Tour> sucheTour(String tourname, ProgressBar progressBar, int maxProgress){
ArrayList<Tour> touren = new ArrayList<>();
getAllTours().forEach(t ->{
getAllTours(progressBar, maxProgress).forEach(t ->{
ArrayList<Log> logs = t.getLogs();
ArrayList<String> bemerkungen = new ArrayList<>();
for (Log l:logs) {
@ -187,4 +202,20 @@ public class TourPlaner{
return touren;
}
/**
* Erzeugt eine Neue Log id
* @param logData Alle log daten
* @return Neue log id
*/
public static String newLogId(ObservableList<Log> logData){
AtomicReference<String> newId = new AtomicReference<>();
newId.set(UUID.randomUUID().toString());
logData.forEach(ss -> {
if (ss.getId().equals(newId.get())) {
newId.set(UUID.randomUUID().toString());
}
});
return newId.get();
}
}

View File

@ -167,17 +167,18 @@ public class DbConnect {
/**
* Holt alle Touren aus der Datenbank
* @param progressBar Aktuelle Progress bar
* @param maxLevel Maximales level der progress bar
* @return Null bei fehler, sonst eine List aus den IDs
*/
public ArrayList<Tour> getAllTouren() {
public ArrayList<Tour> getAllTouren(ProgressBar progressBar, int maxLevel) {
this.c = PostgresHelper.con();
String tourname, mapjson, start, ziel;
double dauer, strecke;
ArrayList<Tour> touren = new ArrayList<>();
ProgressBar progressBar = new ProgressBar("Get...");
try {
int tourSize = getTourSize();
int step = progressBar.getProgressSize(tourSize, 100);
int step = progressBar.getProgressSize(tourSize, maxLevel);
stmt = this.c.createStatement();
ResultSet rs = stmt.executeQuery("select * from tour;");
while (rs.next()) {
@ -191,18 +192,18 @@ public class DbConnect {
touren.add(new Tour(tourname, dauer + "", mapjson, strecke, start, ziel, getLogs(tourname)));
progressBar.addProgress(step);
}else {
progressBar.setProgress(100);
progressBar.setProgress(maxLevel);
return null;
}
}
progressBar.setProgress(100);
progressBar.setProgress(maxLevel);
rs.close();
stmt.close();
this.c.close();
return touren;
} catch (Exception e) {
LogHelper.error(e);
progressBar.setProgress(100);
progressBar.setProgress(maxLevel);
return null;
}
}

View File

@ -23,6 +23,10 @@ public class Log {
}
}
public void setId(String id){
this.id = id;
}
public String getRating() {
return rating;
}

View File

@ -68,12 +68,36 @@
<padding>
<Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
</padding>
<AnchorPane HBox.hgrow="ALWAYS">
<Button fx:id="tourAdd" layoutX="58.0" mnemonicParsing="false" onAction="#addTour" text="+" />
<Label layoutX="14.0" layoutY="4.0" text="Tours" />
<Button fx:id="tourDel" layoutX="89.0" mnemonicParsing="false" onAction="#delTour" text="-" />
<Button fx:id="editTourBtn" layoutX="117.0" mnemonicParsing="false" onAction="#editTourBtn" text="Edit" />
</AnchorPane>
<HBox>
<children>
<Label text="Tours:">
<HBox.margin>
<Insets bottom="10.0" left="10.0" right="6.0" top="10.0" />
</HBox.margin>
</Label>
<Button fx:id="tourAdd" mnemonicParsing="false" onAction="#addTour" text="+">
<HBox.margin>
<Insets bottom="6.0" right="6.0" top="6.0" />
</HBox.margin>
</Button>
<Button fx:id="tourDel" mnemonicParsing="false" onAction="#delTour" text="-">
<HBox.margin>
<Insets bottom="6.0" right="6.0" top="6.0" />
</HBox.margin>
</Button>
<Button fx:id="editTourBtn" mnemonicParsing="false" onAction="#editTourBtn" text="Edit">
<HBox.margin>
<Insets bottom="6.0" right="6.0" top="6.0" />
</HBox.margin>
</Button>
<Button fx:id="copyTourBtn" mnemonicParsing="false" onAction="#copyTour" text="Copy">
<HBox.margin>
<Insets bottom="6.0" right="6.0" top="6.0" />
</HBox.margin>
</Button>
</children>
</HBox>
<AnchorPane HBox.hgrow="ALWAYS" />
<TextField fx:id="sucheInput" onKeyPressed="#sucheEnter" promptText="Suche..." />
<Button fx:id="sucheButton" mnemonicParsing="false" onAction="#suche" text="Suchen" />
</HBox>
@ -125,20 +149,27 @@
</padding>
<Label text="Logs:">
<HBox.margin>
<Insets left="10.0" />
<Insets bottom="10.0" left="10.0" right="6.0" top="10.0" />
</HBox.margin>
</Label>
<Button mnemonicParsing="false" onAction="#addLog" text="+" textAlignment="CENTER">
<HBox.margin>
<Insets />
<Insets bottom="6.0" right="6.0" top="6.0" />
</HBox.margin>
</Button>
<Button fx:id="logDel" mnemonicParsing="false" onAction="#delLog" prefWidth="21.0" text="-" textAlignment="CENTER">
<Button fx:id="logDel" mnemonicParsing="false" onAction="#delLog" text="-" textAlignment="CENTER">
<HBox.margin>
<Insets />
<Insets bottom="6.0" right="6.0" top="6.0" />
</HBox.margin>
</Button>
<Button fx:id="editBtn" mnemonicParsing="false" onAction="#editLogBtn" text="Edit" />
<Button fx:id="editBtn" mnemonicParsing="false" onAction="#editLogBtn" text="Edit">
<HBox.margin>
<Insets bottom="6.0" right="6.0" top="6.0" />
</HBox.margin></Button>
<Button fx:id="copyBtn" layoutX="112.0" layoutY="13.0" mnemonicParsing="false" onAction="#copyLogBtn" text="Copy">
<HBox.margin>
<Insets bottom="6.0" right="6.0" top="6.0" />
</HBox.margin></Button>
</HBox>
</AnchorPane>
<AnchorPane>

View File

@ -41,7 +41,21 @@ public class TourplanerController implements Initializable {
public Menu menueFile, menuebearbeiten, menueoptionen, menuesprachen, menuehilfe;
public MenuItem reportsummary, tourreport, beendenButton;
//Elemente
public Button sucheButton, editBtn, editTourBtn;
public Button sucheButton, editBtn, editTourBtn, copyTourBtn, copyBtn;
@FXML
private void copyTour(){
ProgressBar progressBar = new ProgressBar("Copy");
this.viewModel.copyTour(progressBar, 80);
syncTourNamen(progressBar, 90);
tourFocusOnSelected(progressBar, 100);
}
@FXML
private void copyLogBtn(){
this.viewModel.copyLog();
syncLogs();
}
@FXML
private void onlangenglisch(){
@ -109,9 +123,9 @@ public class TourplanerController implements Initializable {
mapImageView.setImage(this.viewModel.getImage(selectedItem));
}
private void syncTourNamen(){
private void syncTourNamen(ProgressBar progressBar, int maxLevel){
TourListView.getItems().clear();
TourListView.setItems(this.viewModel.getTourNamen());
TourListView.setItems(this.viewModel.getTourNamen(progressBar, maxLevel));
}
private void syncLogs(){
@ -128,12 +142,13 @@ public class TourplanerController implements Initializable {
private void tourListSelectedItem(MouseEvent mouseEvent){
//Beschreibung
ProgressBar progressBar = new ProgressBar("Tour auswählen...");
int steps = progressBar.getProgressSize(7, 100);
String selectedItem = TourListView.getSelectionModel().getSelectedItem();
this.viewModel.selectTour(selectedItem);
titleTextView.setText(selectedItem);
progressBar.addProgress(10);
progressBar.addProgress(steps);
syncTour(selectedItem);
progressBar.addProgress(10);
progressBar.addProgress(steps);
startCol.setCellValueFactory(new PropertyValueFactory<>("start"));
zielCol.setCellValueFactory(new PropertyValueFactory<>("ziel"));
dauerCol.setCellValueFactory(new PropertyValueFactory<>("dauer"));
@ -143,9 +158,9 @@ public class TourplanerController implements Initializable {
//Log anzeigen
logTableView.setPlaceholder(new Label( ConfigHelper.getLangIniString("keinelogsvorhanden")));
logTableView.getItems().clear();
progressBar.addProgress(10);
progressBar.addProgress(steps);
logTableView.setItems(this.viewModel.getLogData());
progressBar.addProgress(10);
progressBar.addProgress(steps);
logDauerCol.setCellValueFactory(new PropertyValueFactory<>("dauer"));
logStreckeCol.setCellValueFactory(new PropertyValueFactory<>("strecke"));
logDatumCol.setCellValueFactory(new PropertyValueFactory<>("datum"));
@ -155,28 +170,33 @@ public class TourplanerController implements Initializable {
logGegangenCol.setCellValueFactory(new PropertyValueFactory<>("gegangen"));
logBemerkungCol.setCellValueFactory(new PropertyValueFactory<>("bemerkung"));
logRatingCol1.setCellValueFactory(new PropertyValueFactory<>("rating"));
progressBar.addProgress(10);
progressBar.addProgress(steps);
mapImageView.setImage(this.viewModel.getImage(this.viewModel.getSelectedTour().getName()));
if(this.viewModel.isSucheAktiv()){
this.viewModel.setSucheAktiv(false);
progressBar.addProgress(10);
syncTourNamen();
progressBar.setProgress(100);
syncTourNamen(progressBar, 100 - steps);
this.sucheInput.setText("");
//Tour namen select focus
AtomicInteger index = new AtomicInteger();
AtomicInteger indexF = new AtomicInteger();
this.viewModel.getTourNamen().forEach(tn -> {
index.getAndIncrement();
if(tn.equals(this.viewModel.getSelectedTour().getName())){
indexF.set(index.get());
}
});
TourListView.getFocusModel().focus(indexF.intValue() - 1);
tourFocusOnSelected(progressBar, 100);
}
progressBar.setProgress(100);
}
/**
* Setzt den Focus auf die ausgewählte Tour
*/
private void tourFocusOnSelected(ProgressBar progressBar, int maxLevel){
//Tour namen select focus
AtomicInteger index = new AtomicInteger();
AtomicInteger indexF = new AtomicInteger();
this.viewModel.getTourNamen(progressBar, maxLevel).forEach(tn -> {
index.getAndIncrement();
if(tn.equals(this.viewModel.getSelectedTour().getName())){
indexF.set(index.get());
}
});
TourListView.getFocusModel().focus(indexF.intValue() - 1);
}
/**
* Beendet die App
* Verbunden mit dem Menu -> Datei -> Beenden
@ -215,8 +235,7 @@ public class TourplanerController implements Initializable {
@FXML
private void suche(){
ProgressBar progressBar = new ProgressBar("Suche");
this.viewModel.suche(this.sucheInput.getText());
progressBar.addProgress(100);
this.viewModel.suche(this.sucheInput.getText(), progressBar, 100);
progressBar.closeProgress();
}
@ -262,7 +281,7 @@ public class TourplanerController implements Initializable {
//Tour list -> links
TourListView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
TourListView.setOrientation(Orientation.VERTICAL);
syncTourNamen();
syncTourNamen(new ProgressBar("Start"), 100);
deselectAll();
if(ConfigHelper.getIniInt(ConfigHelper.getStandartConfig(), "settings", "openpdf") == 1){
this.openpdf.setSelected(true);
@ -288,6 +307,9 @@ public class TourplanerController implements Initializable {
langToFxml();
}
/**
* Setzt jedem Element den Text der gewählten Sprache
*/
private void langToFxml(){
this.kartenTab.setText(ConfigHelper.getLangIniString("fkarte"));
this.beschreibungTab.setText(ConfigHelper.getLangIniString("fbeschreibung"));
@ -306,7 +328,6 @@ public class TourplanerController implements Initializable {
this.logHightCol.setText(ConfigHelper.getLangIniString("fhight"));
this.logPauseCol.setText(ConfigHelper.getLangIniString("fpause"));
this.logStreckeCol.setText(ConfigHelper.getLangIniString("fstrecke"));
this.sucheButton.setText(ConfigHelper.getLangIniString("fsuche"));
this.openmap.setText(ConfigHelper.getLangIniString("fopenmapsetting"));
this.openpdf.setText(ConfigHelper.getLangIniString("fopenpdfsetting"));
@ -320,9 +341,11 @@ public class TourplanerController implements Initializable {
this.reportsummary.setText(ConfigHelper.getLangIniString("freposum"));
this.tourreport.setText(ConfigHelper.getLangIniString("frepotour"));
this.beendenButton.setText(ConfigHelper.getLangIniString("fbeenden"));
this.sucheInput.setPromptText(ConfigHelper.getLangIniString("fsuche")+ConfigHelper.getLangIniString("fpunkte"));
this.sucheInput.setPromptText(ConfigHelper.getLangIniString("fsuche") + ConfigHelper.getLangIniString("fpunkte"));
this.logRatingCol1.setText(ConfigHelper.getLangIniString("rating"));
this.logRatingCol1.setText("Rating");
this.copyBtn.setText(ConfigHelper.getLangIniString("copybtn"));
this.copyTourBtn.setText(ConfigHelper.getLangIniString("copybtn"));
}
@FXML
@ -354,9 +377,10 @@ public class TourplanerController implements Initializable {
@FXML
private void importBtn(){
ProgressBar progressBar = new ProgressBar("Import");
deselectAll();
this.viewModel.importData();
syncTourNamen();
this.viewModel.importData(progressBar, 90);
syncTourNamen(progressBar, 100);
}
@FXML

View File

@ -356,12 +356,7 @@ public class ViewModel {
this.tourData.forEach(s -> {
if (s.getName().equals(this.selectedTour.getName())) {
AtomicReference<String> newId = new AtomicReference<>();
newId.set(UUID.randomUUID().toString());
this.logData.forEach(ss -> {
if (ss.getId().equals(newId.get())) {
newId.set(UUID.randomUUID().toString());
}
});
newId.set(TourPlaner.newLogId(this.logData));
double dauer = dauerInput(-1.0);
double strecke, pause, hightmeter;
boolean bemerkung = false;
@ -476,17 +471,17 @@ public class ViewModel {
return selectedTour;
}
public ObservableList<String> getTourNamen() {
public ObservableList<String> getTourNamen(ProgressBar progressBar, int maxLevel) {
ArrayList<String> namen = new ArrayList<>();
getTourData().forEach(s -> namen.add(s.getName()));
getTourData(progressBar, maxLevel).forEach(s -> namen.add(s.getName()));
tourNamen.clear();
tourNamen.addAll(namen);
return tourNamen;
}
public ObservableList<Tour> getTourData() {
public ObservableList<Tour> getTourData(ProgressBar progressBar, int maxLevel) {
tourData.clear();
ArrayList<Tour> touren = TourPlaner.getAllTours();
ArrayList<Tour> touren = TourPlaner.getAllTours(progressBar, maxLevel);
if(touren != null) {
tourData.addAll(touren);
}
@ -521,18 +516,21 @@ public class ViewModel {
* Sucht eine Tour
* @param sucheInput Text nach dem gesucht werden soll
*/
public void suche(String sucheInput){
public void suche(String sucheInput, ProgressBar progressBar, int maxProgress){
if(sucheInput.isEmpty()){
AlertHelper.warn(ConfigHelper.getLangIniString("achtung"),
ConfigHelper.getLangIniString("suchfeldleer"),
ConfigHelper.getLangIniString("suchtextzuerst"));
}
ArrayList<Tour> result = TourPlaner.sucheTour(sucheInput);
ArrayList<Tour> result = TourPlaner.sucheTour(sucheInput, progressBar, maxProgress/2);
int steps = progressBar.getProgressSize(result.size(), maxProgress);
tourNamen.clear();
for (Tour tour:result) {
tourNamen.add(tour.getName());
progressBar.addProgress(steps);
}
this.sucheAktiv = true;
progressBar.setProgress(maxProgress);
}
public boolean isSucheAktiv() {
@ -626,11 +624,11 @@ public class ViewModel {
/**
* Importiert alle daten von einem File das hier gewählt wird
*/
public void importData() {
public void importData(ProgressBar progressBar, int maxProgress) {
String file = AlertHelper.fileChooser("Importiere");
if (file != null){
this.tourNamen = FXCollections.observableArrayList();
new Exporter(file).doImport();
new Exporter(file).doImport(progressBar, maxProgress);
}
}
@ -638,6 +636,64 @@ public class ViewModel {
* Erstellt einen Summary Report
*/
public void sumReport(){
Reporter.sumReport();
Reporter.sumReport(new ProgressBar("Report"), 100);
}
/**
* Gibt das Selected Log zurück
* @return Null wenn nichts selected ist
*/
public Log getSelectedLog() {
return selectedLog;
}
/**
* Kopiert einen Log eintrag und erstellt dafür eine neue Id für das kopierte log
*/
public void copyLog() {
Log selectedLog = getSelectedLog();
Tour selectedTour = getSelectedTour();
if(selectedLog == null && selectedTour == null){
//TODO kein log & keine tour selected error alert
}else {
selectedLog.setId(TourPlaner.newLogId(this.logData));
TourPlaner.addLog(selectedTour.getName(), selectedLog);
}
}
/**
* Kopiert eie Tour und hängt dafür am ende des namens ein _copy an
* @return Der neue Tour name, bei error ""
*/
public String copyTour(ProgressBar progressBar, int maxLevel) {
Tour selectedTour = getSelectedTour();
String newName = "";
if (selectedTour == null){
//TODO keine tour selected
return "";
}else {
newName = selectedTour.getName() + "_copy";
try {
ArrayList<Log> logs = TourPlaner.getLogs(selectedTour.getName());
progressBar.addProgress(10);
TourPlaner.addTourMax(new Tour(newName, selectedTour.getDauer(), selectedTour.getMapJson(), selectedTour.getStrecke(), selectedTour.getStart(), selectedTour.getZiel(), logs), progressBar, 70);
String finalNewName = newName;
int steps = progressBar.getProgressSize(logs.size(), maxLevel-10);
logs.forEach(l -> {
TourPlaner.addLog(finalNewName, l);
progressBar.addProgress(steps);
});
getTourData(progressBar, maxLevel);
// getLogData();
selectTour(newName);
progressBar.setProgress(maxLevel);
return newName;
} catch (IOException e) {
LogHelper.error(e);
progressBar.setProgress(maxLevel);
return "";
}
}
}
}