/* * ShootOFF - Software for Laser Dry Fire Training * Copyright (C) 2016 phrack * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.shootoff.session.io; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import com.shootoff.camera.shot.DisplayShot; import com.shootoff.camera.shot.ShotColor; import com.shootoff.session.Event; import com.shootoff.session.ExerciseFeedMessageEvent; import com.shootoff.session.ShotEvent; import com.shootoff.session.TargetAddedEvent; import com.shootoff.session.TargetMovedEvent; import com.shootoff.session.TargetRemovedEvent; import com.shootoff.session.TargetResizedEvent; public class XMLSessionReader { private final Logger logger = LoggerFactory.getLogger(XMLSessionReader.class); private final File sessionFile; private long lastTimestamp; private boolean exerciseFeedMessage = false; public XMLSessionReader(File sessionFile) { this.sessionFile = sessionFile; } public Map<String, List<Event>> load() { InputStream xmlInput = null; try { xmlInput = new FileInputStream(sessionFile); final SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser(); final SessionXMLHandler handler = new SessionXMLHandler(); saxParser.parse(xmlInput, handler); return handler.getEvents(); } catch (IOException | ParserConfigurationException | SAXException e) { logger.error("Error reading XML session", e); } finally { if (xmlInput != null) { try { xmlInput.close(); } catch (final IOException e) { logger.error("Error closing XML session opened for reading", e); } } } return new HashMap<>(); } private class SessionXMLHandler extends DefaultHandler { private final Map<String, List<Event>> events = new HashMap<>(); private String currentCameraName = ""; public Map<String, List<Event>> getEvents() { return events; } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { switch (qName) { case "camera": currentCameraName = attributes.getValue("name"); events.put(currentCameraName, new ArrayList<Event>()); break; case "shot": ShotColor c; if (attributes.getValue("color").equals("0xff0000ff") || attributes.getValue("color").equals("RED")) { c = ShotColor.RED; } else if (attributes.getValue("color").equals("0xffa500ff") || attributes.getValue("color").equals("INFRARED")) { c = ShotColor.INFRARED; } else { c = ShotColor.GREEN; } final DisplayShot shot = new DisplayShot(c, Double.parseDouble(attributes.getValue("x")), Double.parseDouble(attributes.getValue("y")), Long.parseLong(attributes.getValue("shotTimestamp")), Integer.parseInt(attributes.getValue("markerRadius"))); final boolean isMalfunction = Boolean.parseBoolean(attributes.getValue("isMalfunction")); final boolean isReload = Boolean.parseBoolean(attributes.getValue("isReload")); Optional<Integer> targetIndex; int index = Integer.parseInt(attributes.getValue("targetIndex")); if (index == -1) { targetIndex = Optional.empty(); } else { targetIndex = Optional.of(index); } Optional<Integer> hitRegionIndex; index = Integer.parseInt(attributes.getValue("hitRegionIndex")); if (index == -1) { hitRegionIndex = Optional.empty(); } else { hitRegionIndex = Optional.of(index); } final Optional<String> videoString = Optional.ofNullable(attributes.getValue("videos")); events.get(currentCameraName) .add(new ShotEvent(currentCameraName, Long.parseLong(attributes.getValue("timestamp")), shot, isMalfunction, isReload, targetIndex, hitRegionIndex, videoString)); break; case "targetAdded": events.get(currentCameraName).add(new TargetAddedEvent(currentCameraName, Long.parseLong(attributes.getValue("timestamp")), attributes.getValue("name"))); break; case "targetRemoved": events.get(currentCameraName) .add(new TargetRemovedEvent(currentCameraName, Long.parseLong(attributes.getValue("timestamp")), Integer.parseInt(attributes.getValue("index")))); break; case "targetResized": events.get(currentCameraName) .add(new TargetResizedEvent(currentCameraName, Long.parseLong(attributes.getValue("timestamp")), Integer.parseInt(attributes.getValue("index")), Double.parseDouble(attributes.getValue("newWidth")), Double.parseDouble(attributes.getValue("newHeight")))); break; case "targetMoved": events.get(currentCameraName) .add(new TargetMovedEvent(currentCameraName, Long.parseLong(attributes.getValue("timestamp")), Integer.parseInt(attributes.getValue("index")), Integer.parseInt(attributes.getValue("newX")), Integer.parseInt(attributes.getValue("newY")))); break; case "exerciseFeedMessage": lastTimestamp = Long.parseLong(attributes.getValue("timestamp")); exerciseFeedMessage = true; } } @Override public void characters(char ch[], int start, int length) throws SAXException { if (exerciseFeedMessage) { events.get(currentCameraName).add( new ExerciseFeedMessageEvent(currentCameraName, lastTimestamp, new String(ch, start, length))); exerciseFeedMessage = false; } } } }