diff --git a/CHANGELOG.md b/CHANGELOG.md
index c0319b5..f6fe189 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,9 @@ All notable changes to this project will be documented in this file.
### Security
* Updated dependencies
+### Features
+* Added support for reading messages, using `getMessages()` method (#5)
+
## 1.2.0 - 2019-06-20
### Security
diff --git a/pom.xml b/pom.xml
index ffe38e3..3aaa5e9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
de.stklcode.pubtrans
juraclient
- 1.2.1-SNAPSHOT
+ 1.3.0-SNAPSHOT
UTF-8
diff --git a/src/main/java/de/stklcode/pubtrans/ura/UraClient.java b/src/main/java/de/stklcode/pubtrans/ura/UraClient.java
index f539dcc..e8ae35b 100644
--- a/src/main/java/de/stklcode/pubtrans/ura/UraClient.java
+++ b/src/main/java/de/stklcode/pubtrans/ura/UraClient.java
@@ -17,6 +17,7 @@
package de.stklcode.pubtrans.ura;
import com.fasterxml.jackson.databind.ObjectMapper;
+import de.stklcode.pubtrans.ura.model.Message;
import de.stklcode.pubtrans.ura.model.Stop;
import de.stklcode.pubtrans.ura.model.Trip;
import de.stklcode.pubtrans.ura.reader.AsyncUraTripReader;
@@ -58,14 +59,21 @@ public class UraClient implements Serializable {
private static final String PAR_ESTTIME = "EstimatedTime";
private static final String PAR_TOWARDS = "Towards";
private static final String PAR_CIRCLE = "Circle";
+ private static final String PAR_MSG_UUID = "MessageUUID";
+ private static final String PAR_MSG_TYPE = "MessageType";
+ private static final String PAR_MSG_PRIORITY = "MessagePriority";
+ private static final String PAR_MSG_TEXT = "MessageText";
private static final Integer RES_TYPE_STOP = 0;
private static final Integer RES_TYPE_PREDICTION = 1;
+ private static final Integer RES_TYPE_FLEX_MESSAGE = 2;
private static final Integer RES_TYPE_URA_VERSION = 4;
private static final String[] REQUEST_STOP = {PAR_STOP_NAME, PAR_STOP_ID, PAR_STOP_INDICATOR, PAR_STOP_STATE, PAR_GEOLOCATION};
private static final String[] REQUEST_TRIP = {PAR_STOP_NAME, PAR_STOP_ID, PAR_STOP_INDICATOR, PAR_STOP_STATE, PAR_GEOLOCATION,
PAR_VISIT_NUMBER, PAR_LINE_ID, PAR_LINE_NAME, PAR_DIR_ID, PAR_DEST_NAME, PAR_DEST_TEXT, PAR_VEHICLE_ID, PAR_TRIP_ID, PAR_ESTTIME};
+ private static final String[] REQUEST_MESSAGE = {PAR_STOP_NAME, PAR_STOP_ID, PAR_STOP_INDICATOR, PAR_STOP_STATE, PAR_GEOLOCATION,
+ PAR_MSG_UUID, PAR_MSG_TYPE, PAR_MSG_PRIORITY, PAR_MSG_TEXT};
private final String baseURL;
private final String instantURL;
@@ -309,6 +317,61 @@ public class UraClient implements Serializable {
return stops;
}
+ /**
+ * Get list of messages.
+ *
+ * @return List of messages.
+ * @since 1.3
+ */
+ public List getMessages() {
+ return getMessages(new Query(), null);
+ }
+
+
+ /**
+ * Get list of messages.
+ * If forStops() has been called, those will be used as filter.
+ *
+ * @param query The query.
+ * @return List of trips.
+ * @since 1.3
+ */
+ public List getMessages(final Query query) {
+ return getMessages(query, null);
+ }
+
+ /**
+ * Get list of messages for given stopIDs with result limit.
+ *
+ * @param query The query.
+ * @param limit Maximum number of results.
+ * @return List of trips.
+ * @since 1.3
+ */
+ public List getMessages(final Query query, final Integer limit) {
+ List messages = new ArrayList<>();
+ try (InputStream is = requestInstant(REQUEST_MESSAGE, query);
+ BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
+ String version = null;
+ String line = br.readLine();
+ while (line != null && (limit == null || messages.size() < limit)) {
+ List l = mapper.readValue(line, List.class);
+ /* Check if result exists and has correct response type */
+ if (l != null && !l.isEmpty()) {
+ if (l.get(0).equals(RES_TYPE_URA_VERSION)) {
+ version = l.get(1).toString();
+ } else if (l.get(0).equals(RES_TYPE_FLEX_MESSAGE)) {
+ messages.add(new Message(l, version));
+ }
+ }
+ line = br.readLine();
+ }
+ } catch (IOException e) {
+ throw new IllegalStateException("Failed to read from API", e);
+ }
+ return messages;
+ }
+
/**
* Issue request to instant endpoint and return input stream.
*
@@ -517,5 +580,15 @@ public class UraClient implements Serializable {
public AsyncUraTripReader getTripsStream(List> consumers) throws IOException {
return UraClient.this.getTripsStream(this, consumers);
}
+
+ /**
+ * Get trips for set filters.
+ *
+ * @return List of matching messages.
+ * @since 1.3
+ */
+ public List getMessages() {
+ return UraClient.this.getMessages(this);
+ }
}
}
diff --git a/src/main/java/de/stklcode/pubtrans/ura/model/Message.java b/src/main/java/de/stklcode/pubtrans/ura/model/Message.java
new file mode 100644
index 0000000..24f3ad8
--- /dev/null
+++ b/src/main/java/de/stklcode/pubtrans/ura/model/Message.java
@@ -0,0 +1,165 @@
+package de.stklcode.pubtrans.ura.model;
+
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Entity for a message.
+ *
+ * @author Stefan Kalscheuer
+ * @since 1.3
+ */
+public class Message implements Model {
+ private static final int MSG_UUID = 7;
+ private static final int MSG_TYPE = 8;
+ private static final int MSG_PRIORITY = 9;
+ private static final int MSG_TEXT = 10;
+ private static final int NUM_OF_FIELDS = 11;
+
+ private final Stop stop;
+ private final String uuid;
+ private final Integer type;
+ private final Integer priority;
+ private final String text;
+
+ /**
+ * Construct Message object from complete set of data.
+ *
+ * @param stopID Stop ID.
+ * @param stopName Stop name.
+ * @param stopIndicator Stop Indicator.
+ * @param stopState Stop state.
+ * @param stopLatitude Stop geolocation latitude.
+ * @param stopLongitude Stop geolocation latitude.
+ * @param msgUUID Message UUID.
+ * @param msgType Message type.
+ * @param msgPriority Message priority.
+ * @param msgText Message text.
+ */
+ public Message(final String stopID,
+ final String stopName,
+ final String stopIndicator,
+ final Integer stopState,
+ final Double stopLatitude,
+ final Double stopLongitude,
+ final String msgUUID,
+ final Integer msgType,
+ final Integer msgPriority,
+ final String msgText) {
+ this(new Stop(stopID,
+ stopName,
+ stopIndicator,
+ stopState,
+ stopLatitude,
+ stopLongitude),
+ msgUUID,
+ msgType,
+ msgPriority,
+ msgText);
+ }
+
+ /**
+ * Construct Message object from Stop model and set of additional data.
+ *
+ * @param stop Stop model
+ * @param msgUUID Message UUID.
+ * @param msgType Message type.
+ * @param msgPriority Message priority.
+ * @param msgText Message text.
+ */
+ public Message(final Stop stop,
+ final String msgUUID,
+ final Integer msgType,
+ final Integer msgPriority,
+ final String msgText) {
+ this.stop = stop;
+ this.uuid = msgUUID;
+ this.type = msgType;
+ this.priority = msgPriority;
+ this.text = msgText;
+ }
+
+ /**
+ * Construct Message object from raw list of attributes parsed from JSON.
+ *
+ * @param raw List of attributes from JSON line
+ * @throws IOException Thrown on invalid line format.
+ */
+ public Message(final List raw) throws IOException {
+ this(raw, null);
+ }
+
+ /**
+ * Construct Message object from raw list of attributes parsed from JSON with explicitly specified version.
+ *
+ * @param raw List of attributes from JSON line
+ * @param version API version
+ * @throws IOException Thrown on invalid line format.
+ */
+ public Message(final List raw, final String version) throws IOException {
+ if (raw == null || raw.size() < NUM_OF_FIELDS) {
+ throw new IOException("Invalid number of fields");
+ }
+
+ stop = new Stop(raw);
+
+ if (raw.get(MSG_UUID) instanceof String) {
+ uuid = (String) raw.get(MSG_UUID);
+ } else {
+ throw Model.typeErrorString(MSG_UUID, raw.get(MSG_UUID).getClass());
+ }
+
+ if (raw.get(MSG_TYPE) instanceof Integer) {
+ type = (Integer) raw.get(MSG_TYPE);
+ } else {
+ throw Model.typeError(MSG_TYPE, raw.get(MSG_TYPE).getClass(), "Integer");
+ }
+
+ if (raw.get(MSG_PRIORITY) instanceof Integer) {
+ priority = (Integer) raw.get(MSG_PRIORITY);
+ } else {
+ throw Model.typeError(MSG_PRIORITY, raw.get(MSG_PRIORITY).getClass(), "Integer");
+ }
+
+ if (raw.get(MSG_TEXT) instanceof String) {
+ text = (String) raw.get(MSG_TEXT);
+ } else {
+ throw Model.typeErrorString(MSG_TEXT, raw.get(MSG_TEXT).getClass());
+ }
+ }
+
+ /**
+ * @return The affected stop.
+ */
+ public Stop getStop() {
+ return stop;
+ }
+
+ /**
+ * @return Message's unique identifier.
+ */
+ public String getUuid() {
+ return uuid;
+ }
+
+ /**
+ * @return Message type.
+ */
+ public Integer getType() {
+ return type;
+ }
+
+ /**
+ * @return Message priority. Lower value equals higher priority.
+ */
+ public Integer getPriority() {
+ return priority;
+ }
+
+ /**
+ * @return Message text.
+ */
+ public String getText() {
+ return text;
+ }
+}
diff --git a/src/test/java/de/stklcode/pubtrans/ura/UraClientTest.java b/src/test/java/de/stklcode/pubtrans/ura/UraClientTest.java
index e770153..b327ac4 100644
--- a/src/test/java/de/stklcode/pubtrans/ura/UraClientTest.java
+++ b/src/test/java/de/stklcode/pubtrans/ura/UraClientTest.java
@@ -16,6 +16,7 @@
package de.stklcode.pubtrans.ura;
+import de.stklcode.pubtrans.ura.model.Message;
import de.stklcode.pubtrans.ura.model.Stop;
import de.stklcode.pubtrans.ura.model.Trip;
import net.bytebuddy.ByteBuddy;
@@ -331,6 +332,43 @@ public class UraClientTest {
}
+ @Test
+ public void getMessages() {
+ // Mock the HTTP call.
+ mockHttpToFile("instant_V1_messages.txt");
+
+ // Get messages without filter and verify some values.
+ List messages = new UraClient("mocked")
+ .getMessages();
+ assertThat(messages, hasSize(2));
+ assertThat(messages.get(0).getStop().getId(), is("100707"));
+ assertThat(messages.get(0).getUuid(), is("016e1231d4e30014_100707"));
+ assertThat(messages.get(1).getStop().getName(), is("Herzogenr. Rathaus"));
+ assertThat(messages.get(1).getUuid(), is("016e2cc3a3750006_210511"));
+ assertThat(messages.get(0).getType(), is(0));
+ assertThat(messages.get(1).getPriority(), is(0));
+ assertThat(messages.get(0).getText(), is("Sehr geehrte Fahrgäste, wegen Strassenbauarbeiten kann diese Haltestelle nicht von den Bussen der Linien 17, 44 und N2 angefahren werden."));
+ assertThat(messages.get(1).getText(), is("Sehr geehrte Fahrgäste, diese Haltestelle wird vorübergehend von den Linien 47, 147 und N3 nicht angefahren."));
+ }
+
+ @Test
+ public void getMessagesForStop() {
+ // Mock the HTTP call.
+ mockHttpToFile("instant_V2_messages_stop.txt");
+
+ // Get trips for stop ID 100707 (Berensberger Str.) and verify some values.
+ List messages = new UraClient("mocked")
+ .forStops("100707")
+ .getMessages();
+ assertThat(messages, hasSize(1));
+ assertThat(messages.stream().filter(t -> !t.getStop().getId().equals("100707")).findAny(), is(Optional.empty()));
+ assertThat(messages.get(0).getUuid(), is("016e1231d4e30014_100707"));
+ assertThat(messages.get(0).getType(), is(0));
+ assertThat(messages.get(0).getPriority(), is(3));
+ assertThat(messages.get(0).getText(), is("Sehr geehrte Fahrgäste, wegen Strassenbauarbeiten kann diese Haltestelle nicht von den Bussen der Linien 17, 44 und N2 angefahren werden."));
+ }
+
+
private static void mockHttpToFile(String newResourceFile) {
mockResource = newResourceFile;
}
diff --git a/src/test/java/de/stklcode/pubtrans/ura/model/MessageTest.java b/src/test/java/de/stklcode/pubtrans/ura/model/MessageTest.java
new file mode 100644
index 0000000..f7dd17d
--- /dev/null
+++ b/src/test/java/de/stklcode/pubtrans/ura/model/MessageTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2016-2019 Stefan Kalscheuer
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package de.stklcode.pubtrans.ura.model;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * Unit test for the {@link Message} meta model.
+ *
+ * @author Stefan Kalscheuer
+ */
+public class MessageTest {
+ @Test
+ public void basicConstructorTest() {
+ Message message = new Message("sid",
+ "name",
+ "indicator",
+ 1,
+ 2.345,
+ 6.789,
+ "msg_uuid",
+ 1,
+ 3,
+ "message text");
+ assertThat(message.getStop().getId(), is("sid"));
+ assertThat(message.getStop().getName(), is("name"));
+ assertThat(message.getStop().getIndicator(), is("indicator"));
+ assertThat(message.getStop().getState(), is(1));
+ assertThat(message.getStop().getLatitude(), is(2.345));
+ assertThat(message.getStop().getLongitude(), is(6.789));
+ assertThat(message.getUuid(), is("msg_uuid"));
+ assertThat(message.getType(), is(1));
+ assertThat(message.getPriority(), is(3));
+ assertThat(message.getText(), is("message text"));
+ }
+
+ @Test
+ public void listConstructorTest() {
+ /* Create valid raw data list */
+ List