package seng302.Mock; import com.sun.org.apache.xpath.internal.operations.Bool; import org.joda.time.DateTime; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import seng302.GPSCoordinate; import seng302.Model.Leg; import seng302.Model.Marker; import seng302.XMLReader; import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.io.InputStream; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.*; /** * Created by jjg64 on 21/04/17. */ public class StreamedCourseXMLReader extends XMLReader { private static double COORDINATEPADDING = 0.000; private GPSCoordinate mapTopLeft, mapBottomRight; private List boundary = new ArrayList<>(); private Map compoundMarks = new HashMap<>(); private Map participants = new HashMap<>(); private List legs = new ArrayList<>(); private List markers = new ArrayList<>(); ZonedDateTime creationTimeDate; ZonedDateTime raceStartTime; int raceID; String raceType; boolean postpone; /** * Constructor for Streamed Race XML * @param filePath path of the file * @throws IOException error * @throws SAXException error * @throws ParserConfigurationException error */ public StreamedCourseXMLReader(String filePath) throws IOException, SAXException, ParserConfigurationException, ParseException, StreamedCourseXMLException { this(filePath, true); } /** * Constructor for Streamed Race XML * @param filePath file path to read * @param read whether or not to read and store the files straight away. * @throws IOException error * @throws SAXException error * @throws ParserConfigurationException error */ public StreamedCourseXMLReader(String filePath, boolean read) throws IOException, SAXException, ParserConfigurationException, ParseException, StreamedCourseXMLException { super(filePath); if (read) { read(); } } public StreamedCourseXMLReader(InputStream xmlString) throws IOException, SAXException, ParserConfigurationException, ParseException, StreamedCourseXMLException { super(xmlString); read(); } private void read() throws ParseException, StreamedCourseXMLException { readRace(); readParticipants(); readCourse(); } private void readRace() throws ParseException { DateTimeFormatter dateFormat = DateTimeFormatter.ISO_OFFSET_DATE_TIME; Element settings = (Element) doc.getElementsByTagName("Race").item(0); System.out.println(getTextValueOfNode(settings, "CreationTimeDate")); raceID = Integer.parseInt(getTextValueOfNode(settings, "RaceID")); raceType = getTextValueOfNode(settings, "RaceType"); creationTimeDate = ZonedDateTime.parse(getTextValueOfNode(settings, "CreationTimeDate"), dateFormat); NamedNodeMap raceTimeTag = doc.getElementsByTagName("RaceStartTime").item(0).getAttributes(); if (raceTimeTag.getNamedItem("Time") != null) raceStartTime = ZonedDateTime.parse(raceTimeTag.getNamedItem("Time").getTextContent(), dateFormat); else raceStartTime = ZonedDateTime.parse(raceTimeTag.getNamedItem("Start").getTextContent(), dateFormat); postpone = Boolean.parseBoolean(raceTimeTag.getNamedItem("Postpone").getTextContent()); } private void readParticipants() { Element nParticipants = (Element) doc.getElementsByTagName("Participants").item(0); nParticipants.getChildNodes().getLength(); for (int i = 0; i < nParticipants.getChildNodes().getLength(); i++) { int sourceID; Node yacht = nParticipants.getChildNodes().item(i); if (yacht.getNodeName().equals("Yacht")) { if (exists(yacht, "SourceID")) { sourceID = Integer.parseInt(yacht.getAttributes().getNamedItem("SourceID").getTextContent()); participants.put(sourceID, new StreamedBoat(sourceID)); } } } } private void readCourse() throws StreamedCourseXMLException { readCompoundMarks(); readCompoundMarkSequence(); readCourseLimit(); } /** * Indexes CompoundMark elements by their ID for use in generating the course, and populates list of Markers. * @see seng302.Model.Marker */ private void readCompoundMarks() throws StreamedCourseXMLException { Element nCourse = (Element) doc.getElementsByTagName("Course").item(0); for(int i = 0; i < nCourse.getChildNodes().getLength(); i++) { Node compoundMark = nCourse.getChildNodes().item(i); if(compoundMark.getNodeName().equals("CompoundMark")) { int compoundMarkID = getCompoundMarkID((Element) compoundMark); compoundMarks.put(compoundMarkID, (Element)compoundMark); markers.add(getMarker(compoundMarkID)); } } } /** * Generates a Marker from the CompoundMark element with given ID. * @param compoundMarkID index of required CompoundMark element * @return generated Marker * @throws StreamedCourseXMLException if CompoundMark element contains unhandled number of compoundMarks * @see seng302.Model.Marker */ private Marker getMarker(int compoundMarkID) throws StreamedCourseXMLException { Element compoundMark = compoundMarks.get(compoundMarkID); NodeList nMarks = compoundMark.getElementsByTagName("Mark"); Marker marker; switch(nMarks.getLength()) { case 1: marker = new Marker(getCoordinate((Element)nMarks.item(0))); break; case 2: marker = new Marker(getCoordinate((Element)nMarks.item(0)), getCoordinate((Element)nMarks.item(1))); break; default: throw new StreamedCourseXMLException(); } return marker; } private GPSCoordinate getCoordinate(Element mark) { double lat = Double.parseDouble(mark.getAttribute("TargetLat")); double lon = Double.parseDouble(mark.getAttribute("TargetLng")); return new GPSCoordinate(lat,lon); } /** * Reads "compoundMarkID" attribute of CompoundMark or Corner element * @param element with "compoundMarkID" attribute * @return value of "compoundMarkID" attribute */ private int getCompoundMarkID(Element element) { //return Integer.parseInt(element.getAttribute("CompoundMarkID")); return 3; } /** * Reads "name" attribute of CompoundMark element with corresponding CompoundMarkID * @param compoundMarkID unique ID for CompoundMark element * @return value of "name" attribute */ private String getCompoundMarkName(int compoundMarkID) { return compoundMarks.get(compoundMarkID).getAttribute("Name"); } /** * Populates list of legs given CompoundMarkSequence element and referenced CompoundMark elements. * @throws StreamedCourseXMLException if markers cannot be resolved from CompoundMark */ private void readCompoundMarkSequence() throws StreamedCourseXMLException { Element nCompoundMarkSequence = (Element) doc.getElementsByTagName("CompoundMarkSequence").item(0); NodeList nCorners = nCompoundMarkSequence.getElementsByTagName("Corner"); Marker lastMarker = getMarker(getCompoundMarkID((Element)nCorners.item(0))); String legName = getCompoundMarkName(getCompoundMarkID((Element)nCorners.item(0))); for(int i = 1; i < nCorners.getLength(); i++) { Element markXML = (Element)nCorners.item(i); Marker currentMarker = getMarker(getCompoundMarkID(markXML)); legs.add(new Leg(legName, lastMarker, currentMarker, i-1)); lastMarker = currentMarker; legName = getCompoundMarkName(getCompoundMarkID(markXML)); } } private void readCourseLimit() { Element nCourseLimit = (Element) doc.getElementsByTagName("CourseLimit").item(0); for(int i = 0; i < nCourseLimit.getChildNodes().getLength(); i++) { Node limit = nCourseLimit.getChildNodes().item(i); if (limit.getNodeName().equals("Limit")) { double lat = Double.parseDouble(limit.getAttributes().getNamedItem("Lat").getTextContent()); double lon = Double.parseDouble(limit.getAttributes().getNamedItem("Lon").getTextContent()); boundary.add(new GPSCoordinate(lat, lon)); } } double maxLatitude = boundary.stream().max(Comparator.comparingDouble(GPSCoordinate::getLatitude)).get().getLatitude() + COORDINATEPADDING; double maxLongitude = boundary.stream().max(Comparator.comparingDouble(GPSCoordinate::getLongitude)).get().getLongitude() + COORDINATEPADDING; double minLatitude = boundary.stream().min(Comparator.comparingDouble(GPSCoordinate::getLatitude)).get().getLatitude() + COORDINATEPADDING; double minLongitude = boundary.stream().min(Comparator.comparingDouble(GPSCoordinate::getLongitude)).get().getLongitude() + COORDINATEPADDING; mapTopLeft = new GPSCoordinate(minLatitude, minLongitude); mapBottomRight = new GPSCoordinate(maxLatitude, maxLongitude); } public List getBoundary() { return boundary; } public GPSCoordinate getMapTopLeft() { return mapTopLeft; } public GPSCoordinate getMapBottomRight() { return mapBottomRight; } public List getLegs() { return legs; } public List getMarkers() { return markers; } public Double getPadding() { return COORDINATEPADDING; } public ZonedDateTime getCreationTimeDate() { return creationTimeDate; } public ZonedDateTime getRaceStartTime() { return raceStartTime; } public int getRaceID() { return raceID; } public String getRaceType() { return raceType; } public boolean isPostpone() { return postpone; } public Map getParticipants() { return participants; } }