/* * 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.courses.io; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; 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.courses.Course; import com.shootoff.gui.LocatedImage; import com.shootoff.gui.targets.TargetView; import com.shootoff.gui.pane.ProjectorArenaPane; import com.shootoff.targets.Target; import com.shootoff.targets.io.TargetIO; import com.shootoff.targets.io.TargetIO.TargetComponents; import javafx.application.Platform; import javafx.geometry.Dimension2D; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; public class XMLCourseReader { private static final Logger logger = LoggerFactory.getLogger(XMLCourseReader.class); private final ProjectorArenaPane arenaPane; private final File courseFile; public XMLCourseReader(ProjectorArenaPane arenaPane, File courseFile) { this.arenaPane = arenaPane; this.courseFile = courseFile; } public Optional<Course> load() { InputStream xmlInput = null; try { xmlInput = new FileInputStream(courseFile); final SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser(); final CourseXMLHandler handler = new CourseXMLHandler(); saxParser.parse(xmlInput, handler); Course c; if (handler.getResolution().isPresent()) { c = new Course(handler.getBackground(), handler.getTargets(), handler.getResolution().get()); } else if (handler.getBackground().isPresent()) { c = new Course(handler.getBackground().get(), handler.getTargets()); } else { c = new Course(handler.getTargets()); } return Optional.of(c); } catch (IOException | ParserConfigurationException | SAXException e) { logger.error("Error reading XML course", e); } finally { if (xmlInput != null) { try { xmlInput.close(); } catch (final IOException e) { logger.error("Error closing XML course opened for reading", e); } } } return Optional.empty(); } private class CourseXMLHandler extends DefaultHandler { private Optional<LocatedImage> background = Optional.empty(); private final List<Target> targets = new ArrayList<>(); private Optional<Dimension2D> resolution = Optional.empty(); public Optional<LocatedImage> getBackground() { return background; } public List<Target> getTargets() { return targets; } public Optional<Dimension2D> getResolution() { return resolution; } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { switch (qName) { case "background": { final boolean isResource = Boolean.parseBoolean(attributes.getValue("isResource")); LocatedImage background; if (isResource) { final InputStream is = this.getClass().getResourceAsStream(attributes.getValue("url")); background = new LocatedImage(is, attributes.getValue("url")); } else { background = new LocatedImage(attributes.getValue("url")); } this.background = Optional.of(background); } break; case "target": { final File targetFile = new File(attributes.getValue("file")); final Optional<TargetComponents> targetComponents = TargetIO.loadTarget(targetFile); if (targetComponents.isPresent()) { final TargetComponents tc = targetComponents.get(); final TargetView t = new TargetView(targetFile, tc.getTargetGroup(), tc.getTargetTags(), arenaPane.getCanvasManager(), true); t.setPosition(Double.parseDouble(attributes.getValue("x")), Double.parseDouble(attributes.getValue("y"))); t.setDimensions(Double.parseDouble(attributes.getValue("width")), Double.parseDouble(attributes.getValue("height"))); targets.add(t); } else { showTargetError(targetFile.getPath()); } } break; case "resolution": { resolution = Optional.of(new Dimension2D(Double.parseDouble(attributes.getValue("width")), Double.parseDouble(attributes.getValue("height")))); } break; } } private void showTargetError(String targetPath) { Platform.runLater(() -> { final Alert targetAlert = new Alert(AlertType.ERROR); final String message = String.format( "The course %s requires the target %s, but the " + "target file is missing. This target will not appear in your projector arena.", courseFile.getName(), targetPath); targetAlert.setTitle("Missing Target"); targetAlert.setHeaderText("Missing Required Target File"); targetAlert.setResizable(true); targetAlert.setContentText(message); targetAlert.show(); }); } } }