From ab0d4634d67e0bc9c064658427f73f3f323c2804 Mon Sep 17 00:00:00 2001 From: Alistair McIntyre Date: Sat, 29 Apr 2017 19:02:30 +1200 Subject: [PATCH] Moved XML parsing to non static class to create objects. Changed the abstraction as using generics in maps lead to more headaches than anything. Still not quite completed. Needs documentation and validation for tags too. #story[820] --- .../seng302/models/parsers/StreamParser.java | 180 +--------- .../seng302/models/parsers/XMLParser.java | 335 ++++++++++++++++++ 2 files changed, 340 insertions(+), 175 deletions(-) create mode 100644 src/main/java/seng302/models/parsers/XMLParser.java diff --git a/src/main/java/seng302/models/parsers/StreamParser.java b/src/main/java/seng302/models/parsers/StreamParser.java index dff14a7f..d3520cf6 100644 --- a/src/main/java/seng302/models/parsers/StreamParser.java +++ b/src/main/java/seng302/models/parsers/StreamParser.java @@ -129,7 +129,7 @@ public class StreamParser { while (payloadStream.available() > 0 && (currentChar = payloadStream.read()) != 0) { xmlMessage += (char)currentChar; } - if (xmlMessageSubType == 6) System.out.println(xmlMessage); + if (xmlMessageSubType == 7) System.out.println(xmlMessage); //Create XML document Object DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); @@ -137,183 +137,13 @@ public class StreamParser { try { db = dbf.newDocumentBuilder(); Document doc = db.parse(new InputSource(new StringReader(xmlMessage))); - switch(xmlMessageSubType) { - case 5: parseRegattaXML(doc); - case 6: parseRaceXML(doc); - case 7: parseBoatXML(doc); + if (xmlMessageSubType == 7) { + XMLParser x = new XMLParser(); + XMLParser.BoatXMLObject xr = x.createBoatXML(doc); } - - } catch (ParserConfigurationException e) { - e.printStackTrace(); - } catch (SAXException e) { - e.printStackTrace(); - } catch (IOException e) { + } catch (ParserConfigurationException|SAXException|IOException e) { e.printStackTrace(); } - - } - - private static void parseRegattaXML(Document doc) { - Element docEle = doc.getDocumentElement(); - String[] regattaElements = {"RegattaID", "RegattaName", "CourseName", "CentralLatitude", "CentralLongitude", - "CentralAltitude", "UtcOffset", "MagneticVariation", "ShorelineName"}; - Map outputMap = parseAtomicElements(docEle, regattaElements); // Regatta contains only atomic elements - - //System.out.println(outputMap); - //return outputMap; - } - - private static void parseRaceXML(Document doc) { - // TODO: 27/04/17 ajm412 This is an extremely long method. Needs to be broken down. - Element docEle = doc.getDocumentElement(); - String[] atomicRaceElements = {"RaceID", "RaceType", "CreationTimeDate"}; - Map outputMap = parseAtomicElements(docEle, atomicRaceElements); - - //Race Start Time - Map raceStartMap = new HashMap<>(); - Node raceStartTime = docEle.getElementsByTagName("RaceStartTime").item(0); - raceStartMap.put("Start", getNodeNamedAttribute(raceStartTime, "Start")); - raceStartMap.put("Postpone", getNodeNamedAttribute(raceStartTime, "Postpone")); - outputMap.put("RaceStartTime", raceStartMap); - - //Race Participants - NodeList participants = docEle.getElementsByTagName("Participants").item(0).getChildNodes(); - outputMap.put("Participants", parseRaceParticipants(participants)); - - //Course (CompoundMarks) - NodeList course = docEle.getElementsByTagName("Course").item(0).getChildNodes(); - outputMap.put("Course", parseCourse(course)); - - //CompoundMark Sequence - NodeList markSequence = docEle.getElementsByTagName("CompoundMarkSequence").item(0).getChildNodes(); - outputMap.put("CourseMarkSequence", parseMarkSequence(markSequence)); - - //Course Limits - NodeList courseLimits = docEle.getElementsByTagName("CourseLimit").item(0).getChildNodes(); - outputMap.put("CourseLimit", parseCourseLimits(courseLimits)); - - System.out.println(outputMap.get("Course")); -// for (Map.Entry entry : outputMap.entrySet()) { -// System.out.println(entry); -// } - } - - private static ArrayList parseRaceParticipants(NodeList participants) { - ArrayList participantList = new ArrayList<>(); - for (int i = 0; i < participants.getLength(); i++) { - Map participantMap = new HashMap<>(); - Integer sourceID = null; - String entry = null; - Node participant = participants.item(i); - if (participant.getNodeName().equals("Yacht")) { - sourceID = Integer.parseInt(getNodeNamedAttribute(participant, "SourceID")); - if (participant.getAttributes().getLength() == 2) { - entry = getNodeNamedAttribute(participant, "Entry"); - } - participantMap.put("sourceID", sourceID); - participantMap.put("Entry", entry); - participantList.add(participantMap); - } - } - return participantList; - } - - private static Map parseCourse(NodeList course) { - Map courseMap = new TreeMap<>(); - ArrayList courseList = new ArrayList<>(); - for (int i = 0; i < course.getLength(); i++) { - - Integer compoundMarkID = null; - String name = null; - //map for an individual CompoundMark - Map compoundMarkMap = new TreeMap<>(); - Node compoundMark = course.item(i); - if (compoundMark.getNodeName().equals("CompoundMark")) { - compoundMarkID = Integer.parseInt(getNodeNamedAttribute(compoundMark, "CompoundMarkID")); - name = getNodeNamedAttribute(compoundMark, "Name"); - //get marks for compound mark - NodeList marks = compoundMark.getChildNodes(); - for (int j = 0; j < marks.getLength(); j++) { - //map for individual mark details within a compound mark - Map markMap = new TreeMap<>(); - Node mark = marks.item(j); - if (mark.getNodeName().equals("Mark")) { - markMap.put("Name", getNodeNamedAttribute(mark, "Name")); - markMap.put("TargetLat", Double.parseDouble(getNodeNamedAttribute(mark, "TargetLat"))); - markMap.put("TargetLng", Double.parseDouble(getNodeNamedAttribute(mark, "TargetLng"))); - markMap.put("SourceID", Integer.parseInt(getNodeNamedAttribute(mark, "SourceID"))); - compoundMarkMap.put(Integer.parseInt(getNodeNamedAttribute(mark, "SeqID")), markMap); - } - } - courseMap.put(compoundMarkID, compoundMarkMap); - } - } - - return courseMap; - } - - private static Map parseMarkSequence(NodeList markSequence) { - Map markSequenceMap = new TreeMap<>(); - - for (int i = 0; i < markSequence.getLength(); i++) { - Map cornerMap = new TreeMap<>(); - Node corner = markSequence.item(i); - if (corner.getNodeName().equals("Corner")) { - cornerMap.put("CompoundMarkID", Integer.parseInt(getNodeNamedAttribute(corner, "CompoundMarkID"))); - cornerMap.put("Rounding", getNodeNamedAttribute(corner, "Rounding")); - cornerMap.put("ZoneSize", getNodeNamedAttribute(corner, "ZoneSize")); - markSequenceMap.put(Integer.parseInt(getNodeNamedAttribute(corner, "SeqID")), cornerMap); - } - } - - return markSequenceMap; - } - - private static Map parseCourseLimits(NodeList courseLimits) { - Map courseLimitMap = new TreeMap<>(); - - for (int i = 0; i < courseLimits.getLength(); i++) { - Map limitMap = new HashMap<>(); - Node limit = courseLimits.item(i); - if (limit.getNodeName().equals("Limit")) { - limitMap.put("Lat", Double.parseDouble(getNodeNamedAttribute(limit,"Lat"))); - limitMap.put("Lon", Double.parseDouble(getNodeNamedAttribute(limit,"Lon"))); - courseLimitMap.put(Integer.parseInt(getNodeNamedAttribute(limit, "SeqID")), limitMap); - } - } - - return courseLimitMap; - } - - private static void parseBoatXML(Document doc) { - // TODO: 27/04/17 ajm412 - } - - private static String getNodeNamedAttribute(Node n, String attr) { - return n.getAttributes().getNamedItem(attr).getTextContent(); - } - - private static Map parseAtomicElements(Element docEle, String[] elements) { - Map outputMap = new HashMap<>(); - for (String element : elements) { - Object elementValue = null; - if (docEle.getElementsByTagName(element).getLength() == 1) { - String elementText = docEle.getElementsByTagName(element).item(0).getTextContent(); - // TODO: 27/04/17 ajm412: this seems messy, trying to parse values as ints/doubles to the map rather than as Strings. Possibly use RegEx. - try { - elementValue = Integer.parseInt(elementText); - } catch (NumberFormatException nfe1) { - try { - elementValue = Double.parseDouble(elementText); - } catch (NumberFormatException nfe2) { - elementValue = elementText; - } - } - } - outputMap.put(element, elementValue); - } - - return outputMap; } private static void extractRaceStartStatus(StreamPacket packet){ diff --git a/src/main/java/seng302/models/parsers/XMLParser.java b/src/main/java/seng302/models/parsers/XMLParser.java new file mode 100644 index 00000000..03e1c3e2 --- /dev/null +++ b/src/main/java/seng302/models/parsers/XMLParser.java @@ -0,0 +1,335 @@ +package seng302.models.parsers; + +import com.sun.org.apache.xpath.internal.SourceTree; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +class XMLParser { + + RegattaXMLObject createRegattaXML(Document doc) { + return new RegattaXMLObject(doc); + } + + RaceXMLObject createRaceXML(Document doc) { + return new RaceXMLObject(doc); + } + + BoatXMLObject createBoatXML(Document doc) { + return new BoatXMLObject(doc); + } + + // TODO: 29/04/17 ajm412: Data Validation here to return null if a tag somehow doesn't actually exist. + private static Integer getElementInt(Element ele, String tag) { + return Integer.parseInt(ele.getElementsByTagName(tag).item(0).getTextContent()); + } + + private static String getElementString(Element ele, String tag) { + return ele.getElementsByTagName(tag).item(0).getTextContent(); + } + + private static Double getElementDouble(Element ele, String tag) { + return Double.parseDouble(ele.getElementsByTagName(tag).item(0).getTextContent()); + } + + private static String getNodeAttributeString(Node n, String attr) { + return n.getAttributes().getNamedItem(attr).getTextContent(); + } + + private static Integer getNodeAttributeInt(Node n, String attr) { + return Integer.parseInt(n.getAttributes().getNamedItem(attr).getTextContent()); + } + + private static Double getNodeAttributeDouble(Node n, String attr) { + return Double.parseDouble(n.getAttributes().getNamedItem(attr).getTextContent()); + } + + class RegattaXMLObject { + + //Regatta Info + private Integer regattaID; + private String regattaName; + private String courseName; + private Double centralLat; + private Double centralLng; + private Integer utcOffset; + + RegattaXMLObject(Document doc) { + Element docEle = doc.getDocumentElement(); + + this.regattaID = getElementInt(docEle, "RegattaID"); + this.regattaName = getElementString(docEle, "RegattaName"); + this.courseName = getElementString(docEle, "CourseName"); + this.centralLat = getElementDouble(docEle, "CentralLatitude"); + this.centralLng = getElementDouble(docEle, "CentralLongitude"); + this.utcOffset = getElementInt(docEle, "UtcOffset"); + } + + public Integer getRegattaID() { return regattaID; } + public String getRegattaName() { return regattaName; } + public String getCourseName() { return courseName; } + public Double getCentralLat() { return centralLat; } + public Double getCentralLng() { return centralLng; } + public Integer getUtcOffset() { return utcOffset; } + + } + + class RaceXMLObject { + + // Race Info + Integer raceID; + String raceType; + String creationTimeDate; // XML Creation Time + + //Race Start Details + String raceStartTime; + Boolean postponeStatus; + + //Non atomic race attributes + ArrayList participants; + ArrayList course; + ArrayList compoundMarkSequence; + ArrayList courseLimit; + + RaceXMLObject(Document doc) { + Element docEle = doc.getDocumentElement(); + participants = new ArrayList<>(); + + NodeList pList = docEle.getElementsByTagName("Participants").item(0).getChildNodes(); + for (int i = 0; i < pList.getLength(); i++) { + Node pNode = pList.item(i); + String entry; + if (pNode.getNodeName().equals("Yacht")) { + Integer sourceID = getNodeAttributeInt(pNode, "SourceID"); + + if (pNode.getAttributes().getLength() == 2) { + entry = getNodeAttributeString(pNode, "Entry"); + } else { + entry = null; + } + + Participant pa = new Participant(sourceID, entry); + participants.add(pa); + } + } + + course = 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")) { + CompoundMark cMark = new CompoundMark(cMarkNode); + course.add(cMark); + } + } + + 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")) { + Corner corner = new Corner(cornerNode); + compoundMarkSequence.add(corner); + } + } + + 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")) { + Limit limit = new Limit(limitNode); + courseLimit.add(limit); + } + } + } + + public ArrayList getParticipants() { return participants; } + public ArrayList getCompoundMarks() { return course; } + public ArrayList getCompoundMarkSequence() { return compoundMarkSequence; } + public ArrayList getCourseLimit() { return courseLimit; } + + class Participant { + Integer sourceID; + String entry; + + Participant(Integer sourceID, String entry) { + this.sourceID = sourceID; + this.entry = entry; + } + + public Integer getsourceID() { return sourceID; } + public String getEntry() { return entry; } + } + + class CompoundMark { + private Integer markID; + private String cMarkName; + private ArrayList marks; + + CompoundMark(Node compoundMark) { + marks = new ArrayList<>(); + this.markID = getNodeAttributeInt(compoundMark, "CompoundMarkID"); + this.cMarkName = 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")) { + Mark mark = new Mark(markNode); + marks.add(mark); + } + } + } + + public Integer getMarkID() { return markID; } + public String getcMarkName() { return cMarkName; } + public ArrayList getMarks() { return marks; } + + class Mark { + private Integer seqID; + private Integer sourceID; + private String markName; + private Double targetLat; + private Double targetLng; + + Mark(Node markNode) { + + this.seqID = getNodeAttributeInt(markNode, "SeqID"); + this.sourceID = getNodeAttributeInt(markNode, "SourceID"); + this.markName = getNodeAttributeString(markNode, "Name"); + this.targetLat = getNodeAttributeDouble(markNode, "TargetLat"); + this.targetLng = getNodeAttributeDouble(markNode, "TargetLng"); + + } + + public Integer getSeqID() { return seqID; } + public Integer getSourceID() { return sourceID; } + public String getMarkName() { return markName; } + public Double getTargetLat() { return targetLat; } + public Double getTargetLng() { return targetLng; } + } + } + + class Corner { + private Integer seqID; + private Integer compoundMarkID; + private String rounding; + private Integer zoneSize; + + Corner(Node cornerNode) { + this.seqID = getNodeAttributeInt(cornerNode, "SeqID"); + this.compoundMarkID = getNodeAttributeInt(cornerNode, "CompoundMarkID"); + this.rounding = getNodeAttributeString(cornerNode, "Rounding"); + this.zoneSize = getNodeAttributeInt(cornerNode, "ZoneSize"); + + } + + public Integer getSeqID() { return seqID; } + public Integer getCompoundMarkID() { return compoundMarkID; } + public String getRounding() { return rounding; } + public Integer getZoneSize() { return zoneSize; } + } + + class Limit { + private Integer seqID; + private Double lat; + private Double lng; + + Limit(Node limitNode) { + this.seqID = getNodeAttributeInt(limitNode, "SeqID"); + this.lat = getNodeAttributeDouble(limitNode, "Lat"); + this.lng = getNodeAttributeDouble(limitNode, "Lon"); + } + + public Integer getSeqID() { return seqID; } + public Double getLat() { return lat; } + public Double getLng() { return lng; } + } + + } + + class BoatXMLObject { + + private String lastModified; + private Integer version; + + //Settings for the boat type in the race. This may end up having to be reworked if multiple boat types compete. + private String boatType; + private Double boatLength; + private Double hullLength; + private Double markZoneSize; + private Double courseZoneSize; + private ArrayList zoneLimits;// will only contain 5 elements. Limits 1-5 + + //Boats + ArrayList boats; + + BoatXMLObject(Document doc) { + + Element docEle = doc.getDocumentElement(); + + this.lastModified = getElementString(docEle, "Modified"); + this.version = getElementInt(docEle, "Version"); + + NodeList settingsList = docEle.getElementsByTagName("Settings").item(0).getChildNodes(); + this.boatType = getNodeAttributeString(settingsList.item(1), "Type"); + this.boatLength = getNodeAttributeDouble(settingsList.item(3), "BoatLength"); + this.hullLength = getNodeAttributeDouble(settingsList.item(3), "HullLength"); + this.markZoneSize = getNodeAttributeDouble(settingsList.item(5), "MarkZoneSize"); + this.courseZoneSize = getNodeAttributeDouble(settingsList.item(5), "CourseZoneSize"); + + Node zoneLimitsList = settingsList.item(7); + this.zoneLimits = new ArrayList<>(); + for (int i = 0; i < zoneLimitsList.getAttributes().getLength(); i++) { + String tag = String.format("Limit%d", i+1); + this.zoneLimits.add(getNodeAttributeDouble(zoneLimitsList, tag)); + } + + } + + public String getLastModified() { return lastModified; } + public Integer getVersion() { return version; } + public String getBoatType() { return boatType; } + public Double getBoatLength() { return boatLength; } + public Double getHullLength() { return hullLength; } + public Double getMarkZoneSize() { return markZoneSize; } + public Double getCourseZoneSize() { return courseZoneSize; } + public ArrayList getZoneLimits() { return zoneLimits; } + public ArrayList getBoats() { return boats; } + + class Boat { + + private String boatType; + private Integer sourceID; + private String hullID; //matches HullNum in the XML spec. + private String shortName; + private String boatName; + private String country; + private String skipper; + + Boat(Node boatNode) { + // TODO: 29/04/17 Actually build the boats. + } + + public String getBoatType() { return boatType; } + public Integer getSourceID() { return sourceID; } + public String getHullID() { return hullID; } + public String getShortName() { return shortName; } + public String getBoatName() { return boatName; } + public String getCountry() { return country; } + public String getSkipper() { return skipper; } + + } + + } + +} \ No newline at end of file