Began fixing bugs with caused by asynchronous listener calls.

#bug
This commit is contained in:
Calum
2017-07-30 20:12:19 +12:00
parent 7894e31926
commit 6cae338c1e
67 changed files with 1370 additions and 1602 deletions
@@ -0,0 +1,431 @@
package seng302.utilities;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import seng302.model.stream.packets.PacketType;
import seng302.model.stream.packets.StreamPacket;
import seng302.model.stream.parser.MarkRoundingData;
import seng302.model.stream.parser.PositionUpdateData;
import seng302.model.stream.parser.PositionUpdateData.DeviceType;
import seng302.model.stream.parser.RaceStartData;
import seng302.model.stream.parser.RaceStatusData;
/**
* StreamParser is a utilities class for taking byte data, formatted according to the AC35
* streaming protocol, and parsing it into basic data types or collections.
*
* Created by kre39 on 23/04/17.
*/
public class StreamParser {
/**
* Extracts and returns the seq num used in the heartbeat packet.
*
* @param packet Packet parsed in to use the payload
* @return the packet sequence number if the packet is of type HEARTBEAT, null otherwise.
*/
public static Long extractHeartBeat(StreamPacket packet) {
if (packet.getType() != PacketType.HEARTBEAT)
return null;
long heartbeat = bytesToLong(packet.getPayload());
System.out.println("heartbeat = " + heartbeat);
return heartbeat;
}
/**
* Extracts the useful race status data from race status type packets. This method will also
* print to the console the current state of the race (if it has started/finished or is about to
* start), along side this it'll also display the amount of time since the race has started or
* time till it starts
*
* @param packet Packet parsed in to use the payload
* @return null if the packet type is not RACE_STATUS, otherwise an instance of RaceStatusData
* containing the parsed packet data.
*/
public static RaceStatusData extractRaceStatus(StreamPacket packet) {
if (packet.getType() != PacketType.RACE_STATUS)
return null;
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
long currentTime = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
long raceId = bytesToLong(Arrays.copyOfRange(payload, 7, 11));
int raceStatus = payload[11];
long expectedStartTime = bytesToLong(Arrays.copyOfRange(payload,12,18));
long windDir = bytesToLong(Arrays.copyOfRange(payload,18,20));
long rawWindSpeed = bytesToLong(Arrays.copyOfRange(payload,20,22));
// DateFormat format = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
// currentTime = format.format((new Date(currentTime)))
RaceStatusData data = new RaceStatusData(
windDir, rawWindSpeed, raceStatus, currentTime, expectedStartTime
);
// long timeTillStart =
// ((new Date(expectedStartTime)).getTime() - (new Date(currentTime)).getTime()) / 1000;
//
// if (timeTillStart > 0) {
// timeSinceStart = timeTillStart;
// } else {
// if (raceStatus == 4 || raceStatus == 8) {
// raceFinished = true;
// raceStarted = false;
// } else if (!raceStarted) {
// raceStarted = true;
// raceFinished = false;
// }
// timeSinceStart = timeTillStart;
// }
//
//
int noBoats = payload[22];
int raceType = payload[23];
long boatID, estTimeAtNextMark, estTimeAtFinish;
int leg, boatStatus;
for (int i = 0; i < noBoats; i++) {
boatID = bytesToLong(
Arrays.copyOfRange(payload, 24 + (i * 20), 28 + (i * 20)));
boatStatus = (int) payload[28 + (i * 20)];
// setBoatLegPosition(boat, (int) payload[29 + (i * 20)]);
// boat.setPenaltiesAwarded((int) payload[30 + (i * 20)]);
// boat.setPenaltiesServed((int) payload[31 + (i * 20)]);
estTimeAtNextMark = bytesToLong(
Arrays.copyOfRange(payload, 32 + (i * 20), 38 + (i * 20)));
// boat.setEstimateTimeTillNextMark(estTimeAtNextMark);
estTimeAtFinish = bytesToLong(
Arrays.copyOfRange(payload, 38 + (i * 20), 44 + (i * 20)));
leg = (int) payload[29 + (i * 20)];
// boat.setEstimateTimeAtFinish(estTimeAtFinish);
data.addBoatData(boatID, estTimeAtNextMark, estTimeAtFinish, leg, boatStatus);
}
return data;
}
// private static void setBoatLegPosition(Yacht updatingBoat, Integer leg){
// Integer placing = 1;
// if (leg != updatingBoat.getLegNumber() && (raceStarted || raceFinished)) {
// for (Yacht boat : boats.values()) {
// if (boat.getLegNumber() != null && leg <= boat.getLegNumber()){
// placing += 1;
// }
// }
// updatingBoat.setPosition(placing.toString());
// updatingBoat.setLegNumber(leg);
// boatsPos.putIfAbsent(placing, updatingBoat);
// boatsPos.replace(placing, updatingBoat);
// } else if(updatingBoat.getLegNumber() == null){
// updatingBoat.setPosition("1");
// updatingBoat.setLegNumber(leg);
// }
// }
/**
* Parses and returns the text from a StreamPacket containing text data for display.
*
* @param packet Packet parsed in to use the payload
* @return A list containing all display message text. Is null if the packet is not of type
* DISPLAY_TEXT_MESSAGE.
*/
public static List<String> extractDisplayMessage(StreamPacket packet) {
if (packet.getType() != PacketType.DISPLAY_TEXT_MESSAGE)
return null;
List<String> message = new ArrayList<>();
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
int numOfLines = payload[3];
int totalLen = 0;
for (int i = 0; i < numOfLines; i++) {
int lineNum = payload[4 + totalLen];
int textLength = payload[5 + totalLen];
byte[] messageTextBytes = Arrays
.copyOfRange(payload, 6 + totalLen, 6 + textLength + totalLen);
message.add(new String(messageTextBytes));
totalLen += 2 + textLength;
}
return message;
}
/**
* Parses and returns an XMLParser containing XML data sent in the given StreamPacket. XML data
* can be for races, boats or the regatta.
*
* @param packet Packet parsed in to use the payload
* @return XMLParse containing xmldata. Returns null if the StreamPacket is not of type
* XML_MESSAGE.
*/
public static Document extractXmlMessage(StreamPacket packet) {
if ( packet.getType() != PacketType.RACE_XML &&
packet.getType() != PacketType.REGATTA_XML &&
packet.getType() != PacketType.BOAT_XML )
return null;
byte[] payload = packet.getPayload();
int messageType = payload[9];
long messageLength = bytesToLong(Arrays.copyOfRange(payload, 12, 14));
String xmlMessage = new String(
(Arrays.copyOfRange(payload, 14, (int) (14 + messageLength)))).trim();
//Create XML document Object
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db;
Document doc = null;
try {
db = dbf.newDocumentBuilder();
doc = db.parse(new InputSource(new StringReader(xmlMessage)));
} catch (ParserConfigurationException | IOException | SAXException e) {
e.printStackTrace();
}
return doc;
}
/**
* Extracts the race start status from the packet and returns it as a long array.
*
* @param packet Packet parsed in to use the payload
* @return An array of form [raceID, raceStartTime, notificationType, timeStamp] or null if
* the packet type is not of RACE_START_STATUS.
*/
public static RaceStartData extractRaceStartStatus(StreamPacket packet) {
if (packet.getType() != PacketType.RACE_START_STATUS) {
return null;
}
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
long raceStartTime = bytesToLong(Arrays.copyOfRange(payload, 9, 15));
long raceId = bytesToLong(Arrays.copyOfRange(payload, 15, 19));
int notificationType = payload[19];
return new RaceStartData(raceId, raceStartTime, notificationType, timeStamp);
}
/**
* Parses the the byte array in a StreamPacket for yacht events to retrieve the necessary info
* and returns it a an array of longs.
*
* @param packet Packet parsed in to use the payload
* @return the event data in the form [boatID, incidentID, eventID, timeStamp]. Returns null if
* the packet is not of type YACHT_EVENT_CODE.
*/
public static long[] extractYachtEventCode(StreamPacket packet) {
if (packet.getType() != PacketType.YACHT_EVENT_CODE)
return null;
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
long raceId = bytesToLong(Arrays.copyOfRange(payload, 9, 13));
long subjectId = bytesToLong(Arrays.copyOfRange(payload, 13, 17));
long incidentId = bytesToLong(Arrays.copyOfRange(payload, 17, 21));
int eventId = payload[21];
return new long[] {subjectId, incidentId, eventId, timeStamp};
}
/**
* Parses data from a StreamPacket for yacht actions and returns it in a long array.
*
* @param packet Packet parsed in to use the payload
* @return long array of packet data in the form [subjectID, incidentID, eventID, timeStamp].
* Returns null if the packet is not of type YACHT_ACTION_CODE.
*/
public static long[] extractYachtActionCode(StreamPacket packet) {
if (packet.getType() != PacketType.YACHT_ACTION_CODE)
return null;
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
long subjectId = bytesToLong(Arrays.copyOfRange(payload, 9, 13));
long incidentId = bytesToLong(Arrays.copyOfRange(payload, 13, 17));
int eventId = payload[17];
return new long[] {subjectId, incidentId, eventId, timeStamp};
}
/**
* Strips the message from the chatter text type packets.
*
* @param packet Packet parsed in to use the payload
* @return Chatter text message as a string. Returns null if the packet is not of type
* CHATTER_TEXT.
*/
public static String extractChatterText(StreamPacket packet) {
if (packet.getType() != PacketType.CHATTER_TEXT)
return null;
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
int messageType = payload[1];
int length = payload[2];
return new String(Arrays.copyOfRange(payload, 3, 3 + length));
}
/**
* Takes the data from a bot location stream packet and parses the id, timeValid, lat, lon,
* heading and groundspeed into a BoatPositionPacket which is returned.
*
* @param packet Packet parsed in to use the payload
* @return BoatPositionPacket containing important boat information. Returns null if the packet
* is not of type BOAT_LOCATION.
*/
public static PositionUpdateData extractBoatLocation(StreamPacket packet) {
if (packet.getType() != PacketType.BOAT_LOCATION)
return null;
byte[] payload = packet.getPayload();
int deviceType = (int) payload[15];
long timeValid = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
long seq = bytesToLong(Arrays.copyOfRange(payload, 11, 15));
long boatId = bytesToLong(Arrays.copyOfRange(payload, 7, 11));
long rawLat = bytesToLong(Arrays.copyOfRange(payload, 16, 20));
long rawLon = bytesToLong(Arrays.copyOfRange(payload, 20, 24));
//Converts the double to a usable lat/lon
double lat = ((180d * (double) rawLat) / Math.pow(2, 31));
double lon = ((180d * (double) rawLon) / Math.pow(2, 31));
double heading = bytesToLong(Arrays.copyOfRange(payload, 28, 30));
heading = 360.0 / 0xffff * heading; //Convert to degrees.
double groundSpeed = bytesToLong(Arrays.copyOfRange(payload, 38, 40)) / 1000.0;
DeviceType type;
if (deviceType == 1)
type = DeviceType.YACHT_TYPE;
else
type = DeviceType.MARK_TYPE;
return new PositionUpdateData((int) boatId, type, lat, lon, heading, groundSpeed);
}
/**
* Processes a stream packet for a mark rounding and returns the boatID, markID and timestamp.
*
* @param packet The packet containing the payload
* @return an array containing longs. The values are [boatID, markID, timeStamp]. Returns null
* if packet is not of type MARK_ROUNDING.
*/
public static MarkRoundingData extractMarkRounding(StreamPacket packet) {
if (packet.getType() != PacketType.MARK_ROUNDING)
return null;
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
long raceId = bytesToLong(Arrays.copyOfRange(payload, 9, 13));
long subjectId = bytesToLong(Arrays.copyOfRange(payload, 13, 17));
int boatStatus = payload[17];
int roundingSide = payload[18];
int markType = payload[19];
int markId = payload[20];
return new MarkRoundingData((int) subjectId, markId, roundingSide, timeStamp);
}
/**
* Returns a list containing the string value of data within the given stream packet for
* course wind.
*
* @param packet The packet containing the payload
* @return the string values of the wind packet. Returns null if the packet is not of type
* COURSE_WIND.
*/
public static List<String> extractCourseWind(StreamPacket packet) {
if (packet.getType() != PacketType.COURSE_WIND)
return null;
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
int selectedWindId = payload[1];
int loopCount = payload[2];
List<String> windInfo = new ArrayList<>();
for (int i = 0; i < loopCount; i++) {
String wind = "WindId: " + payload[3 + (20 * i)];
wind +=
"\nTime: " + bytesToLong(Arrays.copyOfRange(payload, 4 + (20 * i), 10 + (20 * i)));
wind += "\nRaceId: " + bytesToLong(
Arrays.copyOfRange(payload, 10 + (20 * i), 14 + (20 * i)));
wind += "\nWindDirection: " + bytesToLong(
Arrays.copyOfRange(payload, 14 + (20 * i), 16 + (20 * i)));
wind += "\nWindSpeed: " + bytesToLong(
Arrays.copyOfRange(payload, 16 + (20 * i), 18 + (20 * i)));
wind += "\nBestUpWindAngle: " + bytesToLong(
Arrays.copyOfRange(payload, 18 + (20 * i), 20 + (20 * i)));
wind += "\nBestDownWindAngle: " + bytesToLong(
Arrays.copyOfRange(payload, 20 + (20 * i), 22 + (20 * i)));
wind += "\nFlags: " + String
.format("%8s", Integer.toBinaryString(payload[22 + (20 * i)] & 0xFF))
.replace(' ', '0');
windInfo.add(wind);
}
return windInfo;
}
/**
* Returns the parsed data from a StreamPacket for average wind data.
*
* @param packet The packet containing the payload
* @return The wind data in the form
* [rawPeriod, rawSamplePeriod, period2, speed2, period3, speed3, period4, speed4, timestamp]
* or null if the packet is not of type AVG_WIND.
*/
public static long[] extractAvgWind(StreamPacket packet) {
if (packet.getType() != PacketType.AVG_WIND)
return null;
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
long timeStamp = bytesToLong(Arrays.copyOfRange(payload, 1, 7));
long rawPeriod = bytesToLong(Arrays.copyOfRange(payload, 7, 9));
long rawSamplePeriod = bytesToLong(Arrays.copyOfRange(payload, 9, 11));
long period2 = bytesToLong(Arrays.copyOfRange(payload, 11, 13));
long speed2 = bytesToLong(Arrays.copyOfRange(payload, 13, 15));
long period3 = bytesToLong(Arrays.copyOfRange(payload, 15, 17));
long speed3 = bytesToLong(Arrays.copyOfRange(payload, 17, 19));
long period4 = bytesToLong(Arrays.copyOfRange(payload, 19, 21));
long speed4 = bytesToLong(Arrays.copyOfRange(payload, 21, 23));
return new long[] {
rawPeriod, rawSamplePeriod, period2, speed2, period3, speed3, period4, speed4, timeStamp
};
}
public static void extractBoatAction(StreamPacket packet) {
byte[] payload = packet.getPayload();
int messageVersionNo = payload[0];
long actionType = bytesToLong(Arrays.copyOfRange(payload, 0, 1));
if (actionType == 1) {
System.out.println("VMG");
} else if (actionType == 2) {
System.out.println("SAILS IN");
} else if (actionType == 3) {
System.out.println("SAILS OUT");
} else if (actionType == 4) {
System.out.println("TACK/GYBE");
} else if (actionType == 5) {
System.out.println("UPWIND");
} else if (actionType == 6) {
System.out.println("DOWNWIND");
}
}
/**
* takes an array of up to 7 bytes and returns a positive
* long constructed from the input bytes
*
* @return a positive long if there is less than 7 bytes -1 otherwise
*/
public static long bytesToLong(byte[] bytes) {
long partialLong = 0;
int index = 0;
for (byte b : bytes) {
if (index > 6) {
return -1;
}
partialLong = partialLong | (b & 0xFFL) << (index * 8);
index++;
}
return partialLong;
}
}
@@ -0,0 +1,163 @@
package seng302.utilities;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import seng302.model.stream.xml.generator.Race;
import seng302.model.stream.xml.generator.Regatta;
import seng302.server.messages.XMLMessageSubType;
/**
* An XML generator to generate the Race, Boat, and Regatta XML dynamically
*/
public class XMLGenerator {
private static final String XML_TEMPLATE_DIR = "/server_config/xml_templates";
private static final String REGATTA_TEMPLATE_NAME = "regatta.ftlh";
private static final String BOATS_TEMPLATE_NAME = "boats.ftlh";
private static final String RACE_TEMPLATE_NAME = "race.ftlh";
private Configuration configuration;
private Regatta regatta;
private Race race;
/**
* Set up a configuration instance for Apache Freemake
*/
private void setupConfiguration() {
configuration = new Configuration(Configuration.VERSION_2_3_26);
try {
configuration.setClassForTemplateLoading(getClass(), XML_TEMPLATE_DIR);
} catch (NullPointerException e){
System.out.println("[FATAL] Server could not load XML Template directory, ensure this directory isn't empty");
}
}
/**
* Create an instance of the XML Generator
*/
public XMLGenerator(){
setupConfiguration();
}
/**
* Set the race regatta to send to players
* Note: This must be set before a regatta message can be generated
* @param regatta The race regatta
*/
public void setRegatta(Regatta regatta){
this.regatta = regatta;
}
/**
* Set the race to send to players
* Note: This must be set before a boat or race message can be generated
* @param race The race
*/
public void setRace(Race race){
this.race = race;
}
/**
* Parse an XML template and generate the output as a string
* @param templateName The templates file name
* @param type The XML message sub type
*/
private String parseToXmlString(String templateName, XMLMessageSubType type) throws IOException, TemplateException {
Template template;
ByteArrayOutputStream os = new ByteArrayOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(os);
template = configuration.getTemplate(templateName);
switch (type) {
case REGATTA:
template.process(regatta, writer);
break;
case BOAT:
template.process(race, writer);
break;
case RACE:
template.process(race, writer);
break;
default:
throw new UnsupportedOperationException();
}
try {
return os.toString("UTF-8");
} catch (UnsupportedEncodingException e) {
System.out.println("[FATAL] UTF-8 Not supported");
return null;
}
}
/**
* Get the race regatta as a string
* Note: Regatta must be set before calling this
* @return String containing the regatta XML, null if there was an error
*/
public String getRegattaAsXml(){
String result = null;
if (regatta == null) return null;
try {
result = parseToXmlString(REGATTA_TEMPLATE_NAME, XMLMessageSubType.REGATTA);
} catch (TemplateException e) {
System.out.println("[FATAL] Error parsing regatta");
} catch (IOException e) {
System.out.println("[FATAL] Error reading regatta");
}
return result;
}
/**
* Get the boats XML as a string
* Note: Race must be set before calling this
* @return String containing the boats XML, null if there was an error
*/
public String getBoatsAsXml() {
String result = null;
if (race == null) return null;
try {
result = parseToXmlString(BOATS_TEMPLATE_NAME, XMLMessageSubType.BOAT);
} catch (TemplateException e) {
System.out.println("[FATAL] Error parsing boats");
} catch (IOException e) {
System.out.println("[FATAL] Error reading boats");
}
return result;
}
/**
* Get the race XML as a string
* Note: Race must be set before calling this
* @return String containing the race XML, null if there was an error
*/
public String getRaceAsXml() {
String result = null;
if (race == null) return null;
try {
result = parseToXmlString(RACE_TEMPLATE_NAME, XMLMessageSubType.RACE);
} catch (TemplateException e) {
System.out.println("[FATAL] Error parsing race");
} catch (IOException e) {
System.out.println("[FATAL] Error reading race");
}
return result;
}
}
@@ -0,0 +1,293 @@
package seng302.utilities;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import seng302.model.Corner;
import seng302.model.Limit;
import seng302.model.Yacht;
import seng302.model.mark.GateMark;
import seng302.model.mark.Mark;
import seng302.model.mark.MarkType;
import seng302.model.mark.SingleMark;
import seng302.model.stream.xml.parser.RaceXMLData;
import seng302.model.stream.xml.parser.RegattaXMLData;
/**
* Utilities for parsing XML documents
*/
public class XMLParser {
/**
* Returns the text content of a given child element tag, assuming it exists, as an Integer.
*
* @param ele Document Element with child elements.
* @param tag Tag to find in document elements child elements.
* @return Text content from tag if found, null otherwise.
*/
private static Integer getElementInt(Element ele, String tag) {
NodeList tagList = ele.getElementsByTagName(tag);
if (tagList.getLength() > 0) {
return Integer.parseInt(tagList.item(0).getTextContent());
} else {
return null;
}
}
/**
* Returns the text content of a given child element tag, assuming it exists, as an String.
*
* @param ele Document Element with child elements.
* @param tag Tag to find in document elements child elements.
* @return Text content from tag if found, null otherwise.
*/
private static String getElementString(Element ele, String tag) {
NodeList tagList = ele.getElementsByTagName(tag);
if (tagList.getLength() > 0) {
return tagList.item(0).getTextContent();
} else {
return null;
}
}
/**
* Returns the text content of a given child element tag, assuming it exists, as a Double.
*
* @param ele Document Element with child elements.
* @param tag Tag to find in document elements child elements.
* @return Text content from tag if found, null otherwise.
*/
private static Double getElementDouble(Element ele, String tag) {
NodeList tagList = ele.getElementsByTagName(tag);
if (tagList.getLength() > 0) {
return Double.parseDouble(tagList.item(0).getTextContent());
} else {
return null;
}
}
/**
* Returns the text content of an attribute of a given Node, assuming it exists, as a String.
*
* @param n A node object that should have some attributes
* @param attr The attribute you want to get from the given node.
* @return The String representation of the text content of an attribute in the given node, else
* returns null.
*/
private static String getNodeAttributeString(Node n, String attr) {
Node attrItem = n.getAttributes().getNamedItem(attr);
if (attrItem != null) {
return attrItem.getTextContent();
} else {
return null;
}
}
/**
* Returns the text content of an attribute of a given Node, assuming it exists, as an Integer.
*
* @param n A node object that should have some attributes
* @param attr The attribute you want to get from the given node.
* @return The Integer representation of the text content of an attribute in the given node,
* else returns null.
*/
private static Integer getNodeAttributeInt(Node n, String attr) {
Node attrItem = n.getAttributes().getNamedItem(attr);
if (attrItem != null) {
return Integer.parseInt(attrItem.getTextContent());
} else {
return null;
}
}
/**
* Returns the text content of an attribute of a given Node, assuming it exists, as a Double.
*
* @param n A node object that should have some attributes
* @param attr The attribute you want to get from the given node.
* @return The Double representation of the text content of an attribute in the given node, else
* returns null.
*/
private static Double getNodeAttributeDouble(Node n, String attr) {
Node attrItem = n.getAttributes().getNamedItem(attr);
if (attrItem != null) {
return Double.parseDouble(attrItem.getTextContent());
} else {
return null;
}
}
/**
* Produces a mapping of boat sourceIDS to boat objects created from the given xml document.
* @param doc XML Document Object
* @return Mapping of sourceIds to Boats.
*/
public static Map<Integer, Yacht> parseBoats(Document doc){
Map<Integer, Yacht> competingBoats = new HashMap<>();
Element docEle = doc.getDocumentElement();
NodeList boatsList = docEle.getElementsByTagName("Boats").item(0).getChildNodes();
for (int i = 0; i < boatsList.getLength(); i++) {
Node currentBoat = boatsList.item(i);
if (currentBoat.getNodeName().equals("Boat")) {
// Boat boat = new Boat(currentBoat);
Yacht yacht = new Yacht(XMLParser.getNodeAttributeString(currentBoat, "Type"),
XMLParser.getNodeAttributeInt(currentBoat, "SourceID"),
XMLParser.getNodeAttributeString(currentBoat, "HullNum"),
XMLParser.getNodeAttributeString(currentBoat, "ShortName"),
XMLParser.getNodeAttributeString(currentBoat, "BoatName"),
XMLParser.getNodeAttributeString(currentBoat, "Country"));
if (yacht.getBoatType().equals("Yacht")) {
competingBoats.put(yacht.getSourceId(), yacht);
}
}
}
return competingBoats;
}
/**
* Returns an object containing the data extracted from the given xml formatted document
*
* @param doc XML Document Object
* @return Object containing regatta data
*/
public static RegattaXMLData parseRegatta(Document doc) {
Element docEle = doc.getDocumentElement();
Integer regattaID = XMLParser.getElementInt(docEle, "RegattaID");
String regattaName = XMLParser.getElementString(docEle, "RegattaName");
String courseName = XMLParser.getElementString(docEle, "CourseName");
Double centralLat = XMLParser.getElementDouble(docEle, "CentralLatitude");
Double centralLng = XMLParser.getElementDouble(docEle, "CentralLongitude");
Integer utcOffset = XMLParser.getElementInt(docEle, "UtcOffset");
return new RegattaXMLData(
regattaID, regattaName, courseName, centralLat, centralLng, utcOffset
);
}
/**
* Returns an object containing the data extracted from the given xml formatted document
*
* @param doc XML document
* @return object containing race data
*/
public static RaceXMLData parseRace(Document doc) {
Element docEle = doc.getDocumentElement();
return new RaceXMLData(
extractParticpantIDs(docEle),
extractCompoundMarks(docEle),
extractMarkOrder(docEle),
extractCourseLimit(docEle)
);
}
/**
* Extracts course limit data
*/
private static List<Limit> extractCourseLimit(Element docEle) {
List<Limit> courseLimit = new ArrayList<>();
NodeList limitList = docEle.getElementsByTagName("CourseLimit").item(0).getChildNodes();
for (int i = 0; i < limitList.getLength(); i++) {
Node limitNode = limitList.item(i);
if (limitNode.getNodeName().equals("Limit")) {
courseLimit.add(
new Limit(
XMLParser.getNodeAttributeInt(limitNode, "SeqID"),
XMLParser.getNodeAttributeDouble(limitNode, "Lat"),
XMLParser.getNodeAttributeDouble(limitNode, "lon")
)
);
}
}
return courseLimit;
}
/**
* Extracts course order data
*/
private static List<Corner> extractMarkOrder (Element docEle) {
List<Corner> compoundMarkSequence = new ArrayList<>();
NodeList cornerList = docEle.getElementsByTagName("CompoundMarkSequence").item(0)
.getChildNodes();
for (int i = 0; i < cornerList.getLength(); i++) {
Node cornerNode = cornerList.item(i);
if (cornerNode.getNodeName().equals("Corner")) {
compoundMarkSequence.add(
new Corner(
XMLParser.getNodeAttributeInt(cornerNode, "SeqID"),
XMLParser.getNodeAttributeInt(cornerNode, "CompoundMarkID"),
XMLParser.getNodeAttributeString(cornerNode, "Rounding"),
XMLParser.getNodeAttributeInt(cornerNode, "ZoneSize")
)
);
}
}
return compoundMarkSequence;
}
/**
* Extracts course participants data
*/
private static List<Integer> extractParticpantIDs (Element docEle) {
List<Integer> boatIDs = new ArrayList<>();
NodeList pList = docEle.getElementsByTagName("Participants").item(0).getChildNodes();
for (int i = 0; i < pList.getLength(); i++) {
Node pNode = pList.item(i);
if (pNode.getNodeName().equals("Yacht")) {
boatIDs.add(XMLParser.getNodeAttributeInt(pNode, "SourceID"));
}
}
return boatIDs;
}
/**
* Extracts course mark data
*/
private static List<Mark> extractCompoundMarks(Element docEle) {
List<Mark> allMarks = new ArrayList<>();
NodeList cMarkList = docEle.getElementsByTagName("Course").item(0).getChildNodes();
for (int i = 0; i < cMarkList.getLength(); i++) {
Node cMarkNode = cMarkList.item(i);
if (cMarkNode.getNodeName().equals("CompoundMark")) {
allMarks.add(createMark(cMarkNode));
}
}
return allMarks;
}
/**
* Creates marks objects from the given node
*/
private static Mark createMark(Node compoundMark) {
List<SingleMark> subMarks = new ArrayList<>();
Integer compoundMarkID = XMLParser.getNodeAttributeInt(compoundMark, "CompoundMarkID");
String cMarkName = XMLParser.getNodeAttributeString(compoundMark, "Name");
NodeList childMarks = compoundMark.getChildNodes();
for (int i = 0; i < childMarks.getLength(); i++) {
Node markNode = childMarks.item(i);
if (markNode.getNodeName().equals("Mark")) {
Integer sourceID = XMLParser.getNodeAttributeInt(markNode, "SourceID");
String markName = XMLParser.getNodeAttributeString(markNode, "Name");
Double targetLat = XMLParser.getNodeAttributeDouble(markNode, "TargetLat");
Double targetLng = XMLParser.getNodeAttributeDouble(markNode, "TargetLng");
SingleMark mark = new SingleMark(markName, targetLat, targetLng, sourceID,
compoundMarkID);
subMarks.add(mark);
}
}
if (subMarks.size() == 1) {
return subMarks.get(0);
} else {
return new GateMark( cMarkName, MarkType.OPEN_GATE, subMarks.get(0), subMarks.get(1),
subMarks.get(0).getLatitude(), subMarks.get(0).getLongitude(), compoundMarkID
);
}
}
}