From 69d33e60c1ffe023436f2ea9457cda35ab47f41a Mon Sep 17 00:00:00 2001 From: Stephan Richter Date: Sun, 4 Apr 2021 17:42:55 +0200 Subject: [PATCH] implemented maintenance tasks --- pom.xml | 2 +- resources/css/style.css | 4 + .../translations/Application.de.translation | 10 ++ .../de/srsoftware/web4rail/Application.java | 2 + .../de/srsoftware/web4rail/BaseClass.java | 6 + .../de/srsoftware/web4rail/Constants.java | 1 + .../srsoftware/web4rail/MaintnanceTask.java | 150 ++++++++++++++++++ .../java/de/srsoftware/web4rail/Route.java | 4 + .../de/srsoftware/web4rail/moving/Car.java | 74 ++++++++- .../web4rail/moving/Locomotive.java | 11 +- .../de/srsoftware/web4rail/moving/Train.java | 15 +- .../srsoftware/web4rail/tags/AddSelect.java | 26 +++ 12 files changed, 295 insertions(+), 10 deletions(-) create mode 100644 src/main/java/de/srsoftware/web4rail/MaintnanceTask.java create mode 100644 src/main/java/de/srsoftware/web4rail/tags/AddSelect.java diff --git a/pom.xml b/pom.xml index b1d1b40..3e70a6c 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 de.srsoftware web4rail - 1.4.14 + 1.4.15 Web4Rail jar Java Model Railway Control diff --git a/resources/css/style.css b/resources/css/style.css index 2d980de..6edbca8 100644 --- a/resources/css/style.css +++ b/resources/css/style.css @@ -342,6 +342,10 @@ table tr.disabled:hover td { background: yellow; } +tr.due td{ + background: orange; +} + table.brake-times tr > *{ border-style: solid; border-color: black; diff --git a/resources/translations/Application.de.translation b/resources/translations/Application.de.translation index 618ffba..4293612 100644 --- a/resources/translations/Application.de.translation +++ b/resources/translations/Application.de.translation @@ -130,9 +130,11 @@ disable {} : {} deaktivieren disabled routes : deaktivierte Fahrstraßen DisableEnableBlock : Block (de)aktivieren Display "{}" on {}. : „{}“ auf {} anzeigen. +driven distance : zurückgelegte Strecke Drop : Verwerfen Drop brake times : Bremszeiten löschen Dropped destination of {}. : Ziel von {} verworfen. +due after : fällig ab 1) Duration between 5 {} steps during brake process. : 1) Zeit zwischen 5 {}-Schritten beim Bremsvorgang. EAST : Osten edit : bearbeiten @@ -147,6 +149,7 @@ enable {} : {} aktivieren Engage {} : {} aktivieren EngageDecoupler : Entkuppler aktivieren Enter new name for plan : Neuen Namen für den Plan eingeben +executed : ausgeführt extended address : erweiterte Adresse export : exportieren Faster (10 {}) : 10 {} schneller @@ -156,6 +159,7 @@ Firing {} : starte {} For each {} do : Mit jedem {} forward : vorwärts Found {} routes. : {} Routen gefunden. +free : frei machen FreeStartBlock : Start-Block freigeben Free tiles behind train : Kacheln hinter dem Zug freigeben Fullscreen : Vollbild @@ -170,6 +174,7 @@ If car of train\: inspect car number : Falls Fahrzeug aus Zug: Untersuche Fahrze If checked, tiles behind the train are freed according to the length of the train and the tiles. If it is unchecked, tiles will not get free before route is finished. Falls aktiviert, wird die Strecke anhand von Zug- und Kachel-Länge hinter dem Zug freigegeben, Falls deaktiviert wird die Strecke hinter dem Zug erst bei Abschluss der Route freigegeben. internal contacts : interne Kontakte +Interval : Intervall inverted : invertiert Inverts the direction {} is heading to. : Kehrt die Richtung, in welche {} fährt, um. {} is not a block! : {} ist kein Block! @@ -181,6 +186,7 @@ known locomotives : bekannte Lokomotiven known trains : bekannte Züge Label for state {} : Beschriftung für Status {} Last blocks : Letzte Blöcke +Last execution : letzte Ausführung last parked train : letzten geparkten Zug learn : lernen LEFT : links @@ -195,6 +201,7 @@ Locomotives : Lokomotiven Locomotives and cars : Lokomotiven und Waggons Loop : Wiederholung Lower speed limit : Minimale Geschwindigkeit +Maintenance : Wartung Manage cars : Waggons verwalten Manage locos : Lokomotiven verwalten Manage trains : Züge verwalten @@ -203,6 +210,7 @@ Maximum Speed : Höchstgeschwindigkeit maximum speed vmax : Höchstgeschwindigkeit vmax Maximum train length : maximale Zug-Länge mid speed vmid : mittlere Geschwindigeit vmid +mileage : Laufleistung Minimum delay : minimale Verzögerung Minimum and maximum times (in Miliseconds) trains with the respective tag have to wait in this block. : Minamle und maximale Block-Haltezeit (in Millisekunden) für Züge mit der entsprchender Markierung. minimum starting voltage vmin : Mindestanfahrspannung vmin @@ -351,6 +359,8 @@ Switch power on : Strom anschalten SYSTEM : Betriebssystem Tag : Markierung Tags : Markierungen +Task : Aufgabe +Task type : Aufgabe Text to display on clients : Text, welcher auf den Clients angezeigt werden soll Text to show on display : Text, welcher in der Anzeige dargestellt werden soll Tile(s) : Kachel(n) diff --git a/src/main/java/de/srsoftware/web4rail/Application.java b/src/main/java/de/srsoftware/web4rail/Application.java index 14198e0..27e7364 100644 --- a/src/main/java/de/srsoftware/web4rail/Application.java +++ b/src/main/java/de/srsoftware/web4rail/Application.java @@ -127,6 +127,8 @@ public class Application extends BaseClass{ return plan.controlUnit().process(params); case REALM_LOCO: return Locomotive.action(params,plan); + case REALM_MAINTENANCE: + return MaintnanceTask.action(params); case REALM_PLAN: return plan.action(params); case REALM_ROUTE: diff --git a/src/main/java/de/srsoftware/web4rail/BaseClass.java b/src/main/java/de/srsoftware/web4rail/BaseClass.java index 9cb8bb7..7d6071c 100644 --- a/src/main/java/de/srsoftware/web4rail/BaseClass.java +++ b/src/main/java/de/srsoftware/web4rail/BaseClass.java @@ -2,6 +2,7 @@ package de.srsoftware.web4rail; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.text.SimpleDateFormat; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collections; @@ -567,6 +568,11 @@ public abstract class BaseClass implements Constants{ return Translation.get(Application.class, txt, fills); } + public static String time(Date date) { + return isSet(date) ? new SimpleDateFormat("YYYY-dd-MM HH:mm").format(date) : null; + } + + public static long timestamp() { return new Date().getTime(); } diff --git a/src/main/java/de/srsoftware/web4rail/Constants.java b/src/main/java/de/srsoftware/web4rail/Constants.java index b5fe6bd..6a6ebf0 100644 --- a/src/main/java/de/srsoftware/web4rail/Constants.java +++ b/src/main/java/de/srsoftware/web4rail/Constants.java @@ -47,6 +47,7 @@ public interface Constants { public static final String REALM_CONTACT = "contact"; public static final String REALM_CU = "cu"; public static final String REALM_LOCO = "loco"; + public static final String REALM_MAINTENANCE = "maintenance"; public static final String REALM_ROUTE = "route"; public static final String REALM_PLAN = "plan"; public static final String REALM_TRAIN = "train"; diff --git a/src/main/java/de/srsoftware/web4rail/MaintnanceTask.java b/src/main/java/de/srsoftware/web4rail/MaintnanceTask.java new file mode 100644 index 0000000..7fd4ae6 --- /dev/null +++ b/src/main/java/de/srsoftware/web4rail/MaintnanceTask.java @@ -0,0 +1,150 @@ +package de.srsoftware.web4rail; + +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +import org.json.JSONObject; + +import de.srsoftware.web4rail.moving.Car; +import de.srsoftware.web4rail.moving.Train; +import de.srsoftware.web4rail.tags.AddSelect; +import de.srsoftware.web4rail.tags.Button; +import de.srsoftware.web4rail.tiles.Block; + +public class MaintnanceTask extends BaseClass{ + private static final HashSet tasks = new HashSet<>(); + + private static final String NAME = "name"; + + public static final String INTERVAL = "interval"; + + private static final String LAST_DATE = "last_date"; + + private static final String LAST_DIST = "last_dist"; + + private long interval; + private long lastExecutionDist; + private Date lastExecutionDate; + private String name; + + public MaintnanceTask(Car car, String name, long interval) { + this.name = name; + this.interval = interval; + parent(car); + register(); + if (isSet(name)) tasks.add(name); + } + + public static Object action(HashMap params) { + String action = params.get(ACTION); + if (isNull(action)) return t("No action set!"); + + MaintnanceTask task = BaseClass.get(Id.from(params)); + + switch (action) { + case ACTION_ADD: + Car car = BaseClass.get(Id.from(params, REALM_CAR)); + return car.addTask(createTask(params)); + case ACTION_DROP: + if (isSet(task)) { + BaseClass parent = task.parent(); + task.remove(); + return parent.properties(); + } + return t("No task!"); + case ACTION_START: + return isSet(task) ? task.executed() : t("No task!"); + } + String err = t("unknown action: {}",action); + return (isSet(task)) ? task.parent().properties(err) : err; + } + + private static MaintnanceTask createTask(HashMap params) { + String name = params.get(NAME); + long interval = Long.parseLong(params.get(INTERVAL)); + Car car = BaseClass.get(Id.from(params, REALM_CAR)); + return new MaintnanceTask(car, name,interval); + } + + public Button execBtn() { + return button(t("executed"), Map.of(REALM,REALM_MAINTENANCE,ACTION,ACTION_START)); + } + + private Object executed() { + BaseClass parent = parent(); + if (parent instanceof Car) { + Car car = (Car) parent; + lastExecutionDate = new Date(); + lastExecutionDist = car.drivenDistance(); + Train train = car.train(); + if (isSet(train)) { + Block block = train.currentBlock(); + if (isSet(block)) plan.place(block); + } + return car.properties(); + } + return t("parent is not a car!"); + } + + public long interval() { + return interval; + } + + public boolean isDue() { + return ((Car)parent()).drivenDistance() > lastExecutionDist+interval; + } + + @Override + public JSONObject json() { + JSONObject json = super.json(); + json.put(NAME, name); + json.put(INTERVAL, interval); + if (isSet(lastExecutionDate)) { + json.put(LAST_DATE, lastExecutionDate.getTime()); + json.put(LAST_DIST, lastExecutionDist); + } + return json; + } + + public Date lastDate() { + return lastExecutionDate; + } + + public long lastMileage() { + return lastExecutionDist; + } + + @Override + public MaintnanceTask load(JSONObject json) { + json.remove(ID); // we're using the id created by the constructor! + super.load(json); + name = json.getString(NAME); + interval = json.getLong(INTERVAL); + if (json.has(LAST_DATE)) lastExecutionDate = new Date(json.getLong(LAST_DATE)); + if (json.has(LAST_DIST)) lastExecutionDist = json.getLong(LAST_DIST); + if (isSet(name)) tasks.add(name); + return this; + } + + public long nextMileage() { + return lastExecutionDist+interval; + } + + public Button removeBtn() { + return button(t("delete"), Map.of(REALM,REALM_MAINTENANCE,ACTION,ACTION_DROP)); + } + + public static AddSelect selector() { + AddSelect select = new AddSelect(NAME); + select.addOption(t("create new task type")); + tasks.forEach(task -> select.addOption(task)); + return select; + } + + @Override + public String toString() { + return name; + } +} \ No newline at end of file diff --git a/src/main/java/de/srsoftware/web4rail/Route.java b/src/main/java/de/srsoftware/web4rail/Route.java index 8244396..e787c25 100644 --- a/src/main/java/de/srsoftware/web4rail/Route.java +++ b/src/main/java/de/srsoftware/web4rail/Route.java @@ -492,6 +492,10 @@ public class Route extends BaseClass { return json; } + public long length() { + return path.stream().mapToLong(Tile::length).sum(); + } + private Route load(JSONObject json,Plan plan) { if (json.has(ID)) id = Id.from(json); if (json.has(NAME)) name(json.getString(NAME)); diff --git a/src/main/java/de/srsoftware/web4rail/moving/Car.java b/src/main/java/de/srsoftware/web4rail/moving/Car.java index 731f0f4..b1618dc 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Car.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Car.java @@ -14,12 +14,14 @@ import java.util.TreeSet; import java.util.Vector; import java.util.stream.Collectors; +import org.json.JSONArray; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import de.srsoftware.tools.Tag; import de.srsoftware.web4rail.BaseClass; +import de.srsoftware.web4rail.MaintnanceTask; import de.srsoftware.web4rail.Plan; import de.srsoftware.web4rail.tags.Button; import de.srsoftware.web4rail.tags.Fieldset; @@ -45,7 +47,12 @@ public class Car extends BaseClass implements Comparable{ private static final String MAX_SPEED_REVERSE = "max_speed_reverse"; private static final String ORDER = "order"; private static final String ORIENTATION = "orientation"; - protected HashSet tags = new HashSet(); + + private static final String DRIVEN_DISTANCE = "driven_distance"; + + private static final String MAINTENANCE = "maintenance"; + protected HashSet tags = new HashSet<>(); + private HashSet tasks = new HashSet<>(); private String name; public int length; @@ -54,6 +61,7 @@ public class Car extends BaseClass implements Comparable{ protected int maxSpeedForward = 0; protected int maxSpeedReverse = 0; protected boolean orientation = FORWARD; + protected long distanceCounter = 0; public Car(String name) { this(name,null); @@ -93,6 +101,11 @@ public class Car extends BaseClass implements Comparable{ return t("Unknown action: {}",params.get(ACTION)); } + public Object addTask(MaintnanceTask newTask) { + tasks.add(newTask); + return properties(); + } + public Car clone() { Car clone = new Car(name); clone.maxSpeedForward = maxSpeedForward; @@ -114,6 +127,14 @@ public class Car extends BaseClass implements Comparable{ public int compareTo(Car o) { return (stockId+":"+name).compareTo(o.stockId+":"+o.name); } + + public Object addDistance(long len) { + return distanceCounter+= len; + } + + public long drivenDistance() { + return distanceCounter; + } public JSONObject json() { JSONObject json = super.json(); @@ -123,7 +144,12 @@ public class Car extends BaseClass implements Comparable{ if (maxSpeedReverse != 0) json.put(MAX_SPEED_REVERSE, maxSpeedReverse); json.put(ORIENTATION, orientation); json.put(STOCK_ID, stockId); + if (distanceCounter>0) json.put(DRIVEN_DISTANCE, distanceCounter); if (!tags.isEmpty()) json.put(TAGS, tags); + if (!tasks.isEmpty()) { + json.put(MAINTENANCE, tasks.stream().map(MaintnanceTask::json).collect(Collectors.toList())); + LOG.debug("json: {}",json); + } return json; } @@ -166,9 +192,38 @@ public class Car extends BaseClass implements Comparable{ if (json.has(STOCK_ID)) stockId = json.getString(STOCK_ID); if (json.has(TAGS)) json.getJSONArray(TAGS).forEach(elem -> { tags.add(elem.toString()); }); if (json.has(ORIENTATION)) orientation=json.getBoolean(ORIENTATION); + if (json.has(DRIVEN_DISTANCE)) distanceCounter = json.getLong(DRIVEN_DISTANCE); + if (json.has(MAINTENANCE)) loadMaintenance(json.getJSONArray(MAINTENANCE)); return this; } + private void loadMaintenance(JSONArray arr) { + arr.forEach(o -> { + if (o instanceof JSONObject) { + tasks.add(new MaintnanceTask(this,null,0).load((JSONObject) o)); + } + }); + } + + private Fieldset maintenance() { + Fieldset fieldset = new Fieldset(t("Maintenance")+(needsMaintenance()?NBSP+"⚠":"")); + Table table = new Table(); + table.addHead(t("Task"),t("Last execution"),t("due after"),t("Interval"),t("Actions")); + for (MaintnanceTask task : tasks) { + Tag row = table.addRow(task,time(task.lastDate())+" / "+task.lastMileage()+NBSP+Plan.lengthUnit,task.nextMileage()+NBSP+Plan.lengthUnit,task.interval()+NBSP+Plan.lengthUnit,task.execBtn()+NBSP+task.removeBtn()); + if (task.isDue()) row.clazz("due"); + } + table.addTo(fieldset); + Form form = new Form("create-task"); + new Input(REALM,REALM_MAINTENANCE).hideIn(form); + new Input(ACTION,ACTION_ADD).hideIn(form); + new Input(REALM_CAR,id()).hideIn(form); + MaintnanceTask.selector().addTo(new Label(t("Task type")+NBSP)).addTo(form); + new Input(MaintnanceTask.INTERVAL,1_000_000).numeric().addTo(new Label(t("Interval")+NBSP)).content(NBSP+Plan.lengthUnit).addTo(form); + new Button(t("add"), form).addTo(form); + return form.addTo(fieldset); + } + public static Object manager(Map params) { Window win = new Window("car-manager", t("Car manager")); new Tag("h4").content(t("known cars")).addTo(win); @@ -177,7 +232,7 @@ public class Car extends BaseClass implements Comparable{ String order = params.get(ORDER); Tag nameLink = link("span", t("Name"), Map.of(REALM,REALM_CAR,ACTION,ACTION_PROPS,ORDER,NAME)); - Table table = new Table().addHead(t("Stock ID"),nameLink,t("Max. Speed",speedUnit),t("Length"),t("Train"),t("Tags"),t("Actions")); + Table table = new Table().addHead(t("Stock ID"),nameLink,t("Max. Speed",speedUnit),t("Length"),t("Train"),t("Tags"),t("driven distance"),t("Actions")); List cars = BaseClass.listElements(Car.class) .stream() .filter(car -> !(car instanceof Locomotive)) @@ -210,6 +265,7 @@ public class Car extends BaseClass implements Comparable{ car.length+NBSP+lengthUnit, isSet(car.train) ? car.train.link("span", car.train) : "", String.join(", ", car.tags()), + car.distanceCounter, actions ); } @@ -238,6 +294,12 @@ public class Car extends BaseClass implements Comparable{ return name; } + boolean needsMaintenance() { + for (MaintnanceTask task : tasks) { + if (task.isDue()) return true; + } + return false; + } public boolean orientation() { return orientation; } @@ -252,15 +314,17 @@ public class Car extends BaseClass implements Comparable{ new Input(MAX_SPEED, maxSpeedForward).numeric().addTo(new Tag("p")).content(NBSP+speedUnit+NBSP+t("forward")).addTo(div); new Input(MAX_SPEED_REVERSE, maxSpeedReverse).numeric().addTo(new Tag("p")).content(NBSP+speedUnit+NBSP+t("backward")).addTo(div); formInputs.add(t("Maximum Speed"),div); - if (train != null) formInputs.add(t("Train"), train.link()); + if (isSet(train)) formInputs.add(t("Train"), train.link()); formInputs.add(t("Current orientation"),new Tag("span").content(orientation ? t("forward") : t("reverse"))); - + formInputs.add(t("driven distance"),new Tag("span").content(distanceCounter+" "+Plan.lengthUnit)); + postForm.add(maintenance()); return super.properties(preForm,formInputs,postForm,errors); } - + @Override protected void removeChild(BaseClass child) { if (child == train) train = null; + tasks.remove(child); super.removeChild(child); } diff --git a/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java b/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java index 91f5068..16a8b8b 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Locomotive.java @@ -270,14 +270,21 @@ public class Locomotive extends Car implements Constants,Device{ new Tag("p").content(t("Click on a name to edit the entry.")).addTo(win); - Table table = new Table().addHead(t("Stock ID"),t("Name"),t("Max. Speed",speedUnit),t("Protocol"),t("Address"),t("Length"),t("Tags")); + Table table = new Table().addHead(t("Stock ID"),t("Name"),t("Max. Speed",speedUnit),t("Protocol"),t("Address"),t("Length"),t("driven distance"),t("Tags")); List locos = BaseClass.listElements(Locomotive.class); locos.sort(Comparator.comparing(loco -> loco.address)); locos.sort(Comparator.comparing(loco -> loco.stockId)); for (Locomotive loco : locos) { String maxSpeed = (loco.maxSpeedForward == 0 ? "–":""+loco.maxSpeedForward)+NBSP; if (loco.maxSpeedReverse != loco.maxSpeedForward) maxSpeed += "("+loco.maxSpeedReverse+")"+NBSP; - table.addRow(loco.stockId,loco.link(),maxSpeed+speedUnit,loco.proto,loco.address,loco.length+NBSP+lengthUnit,String.join(", ", loco.tags())); + table.addRow(loco.stockId, + loco.link(), + maxSpeed+NBSP+speedUnit, + loco.proto, + loco.address, + loco.length+NBSP+lengthUnit, + loco.distanceCounter, + String.join(", ", loco.tags())); } table.addTo(win); diff --git a/src/main/java/de/srsoftware/web4rail/moving/Train.java b/src/main/java/de/srsoftware/web4rail/moving/Train.java index cd9223c..bbab858 100644 --- a/src/main/java/de/srsoftware/web4rail/moving/Train.java +++ b/src/main/java/de/srsoftware/web4rail/moving/Train.java @@ -229,7 +229,7 @@ public class Train extends BaseClass implements Comparable { boolean first = true; for (Car car : cars) { - Tag link = car.link(car.name()+(car.stockId.isEmpty() ? "" : " ("+car.stockId+")")); + Tag link = car.link(car.name()+(car.needsMaintenance()?"⚠":"")+(car.stockId.isEmpty() ? "" : " ("+car.stockId+")")); Tag buttons = new Tag("span"); car.button(t("turn within train"),Map.of(ACTION,ACTION_TURN)).addTo(buttons); @@ -390,6 +390,7 @@ public class Train extends BaseClass implements Comparable { public String directedName() { String result = name(); + if (needsMainenance()) result+="⚠"; String mark = autopilot ? "ⓐ" : ""; if (isNull(direction)) return result; switch (direction) { @@ -516,6 +517,9 @@ public class Train extends BaseClass implements Comparable { } }; } + + long len = endedRoute.length(); + if (len>0) cars.forEach(car -> car.addDistance(len)); } private Tag faster(int steps) { @@ -733,6 +737,13 @@ public class Train extends BaseClass implements Comparable { return this; } + private boolean needsMainenance() { + for (Car car: cars) { + if (car.needsMaintenance()) return true; + } + return false; + } + public boolean onTrace(Tile t) { return trace.contains(t); } @@ -783,7 +794,7 @@ public class Train extends BaseClass implements Comparable { formInputs.add(t("Tags"), new Input(TAGS,String.join(", ", tags))); if (this.hasLoco()) preForm.add(Locomotive.cockpit(this)); - postForm.add(propList.addTo(new Fieldset(t("other train properties")).id("props-other"))); + postForm.add(propList.addTo(new Fieldset(t("other train properties")+(needsMainenance()?NBSP+"⚠":"")).id("props-other"))); postForm.add(brakeTimes()); postForm.add(blockHistory()); diff --git a/src/main/java/de/srsoftware/web4rail/tags/AddSelect.java b/src/main/java/de/srsoftware/web4rail/tags/AddSelect.java new file mode 100644 index 0000000..d3b28a8 --- /dev/null +++ b/src/main/java/de/srsoftware/web4rail/tags/AddSelect.java @@ -0,0 +1,26 @@ +package de.srsoftware.web4rail.tags; + +import de.srsoftware.tools.Tag; + +public class AddSelect extends Input { + private static final long serialVersionUID = -2168654457876014503L; + private Tag dataList; + + public AddSelect(String name) { + super("select"); + attr("name",name); + attr("list","list-options"); + dataList = new Tag("datalist").id("list-options"); + dataList.addTo(this); + } + + public Tag addOption(Object value) { + return addOption(value, value); + } + + public Tag addOption(Object value, Object text) { + Tag option = new Tag("option").attr("value", value.toString()).content(text.toString()); + option.addTo(dataList); + return option; + } +} \ No newline at end of file