Package org.kalimullin.fsraytracer.data

Source Code of org.kalimullin.fsraytracer.data.SceneDataXpathParser

package org.kalimullin.fsraytracer.data;

import com.sun.org.apache.xerces.internal.parsers.DOMParser;
import org.kalimullin.fsraytracer.geometry.Face;
import org.kalimullin.fsraytracer.geometry.Point;
import org.kalimullin.fsraytracer.geometry.Polygon;
import org.kalimullin.fsraytracer.scene.PolygonalSceneObject;
import org.kalimullin.fsraytracer.scene.SceneObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.xpath.*;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class SceneDataXpathParser implements SceneDataProvider {

    // TODO refactor this class (to factory with interface implementations)
    public SceneDataXpathParser(File file) {
        try {
            logger.info("Initializing XML Parsing");
            this.document = getParsedDocument(file);
        } catch (SAXException e) {
            logger.error("There was an error while parsing XML: ", e);
        }
    }

    private final static Logger logger = LoggerFactory.getLogger(SceneDataProvider.class);
    //TODO switch to SAX parser. XPath is not necessary for this structure
    private XPathFactory xPathFactory = XPathFactory.newInstance();
    private XPath xpath = xPathFactory.newXPath();
    //TODO null handling
    private Document document = null;

    private void validate(File file) throws SAXException {
        SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        Schema schema = sf.newSchema(new File("scene.xsd"));
        Validator validator = schema.newValidator();
        try {
            // Actually double-creating StreamSource can cause performance problems while parsing really big files.
            // If this is really big deal, this method should be inlined
            validator.validate(new StreamSource(file));
        } catch (IOException e) {
            // TODO somehow get rid of IOException by refactoring all Source-related operations
            logger.error("IOException while parsing XML: ", e);
        }
    }

    /**
     * Parsing Scene XML with validation.
     * @param file input XML
     * @return parsed Document
     * @throws SAXException if XML is invalid
     */
    private Document getParsedDocument(File file) throws SAXException {
        validate(file);
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        try {
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            return documentBuilder.parse(file);
        } catch (ParserConfigurationException | IOException e) {
            logger.error("Exception while parsing file {}:", file, e);
        }
        return new DOMParser().getDocument();
    }


    /**
     * Parsing scene objects from XML.
     * @return List<SceneObject> with PolygonalSceneObject
     */
    public Set<SceneObject> getSceneObjects() {
        Set<SceneObject> sceneObjectSet = new HashSet<>();
        Map<Integer, Polygon> polygonMap = getPolygons();
        try {
            XPathExpression xPathExpression = xpath.compile("/scene/objects/object");
            NodeList objects = (NodeList) xPathExpression.evaluate(document, XPathConstants.NODESET);
            logger.trace("Objects NodeList size: {}", objects.getLength());
            for (int facesIndex = 0; facesIndex < objects.getLength(); facesIndex++) {
                Node faceNode = objects.item(facesIndex);
                String name = (String)xpath.evaluate("name", faceNode, XPathConstants.STRING);
                int polygonsCount = ((Number)xpath.evaluate("count(face-id)", faceNode, XPathConstants.NUMBER)).intValue();
                Set<Polygon> polygonSet = new HashSet<>();
                for(int polygonIndex = 1; polygonIndex <= polygonsCount; polygonIndex++) {
                    polygonSet.add(polygonMap.get(((Number) xpath.evaluate("(face-id)[" + polygonIndex + "]",
                            faceNode, XPathConstants.NUMBER)).intValue()));
                }
                PolygonalSceneObject polygonalSceneObject = new PolygonalSceneObject(name, polygonSet);
                sceneObjectSet.add(polygonalSceneObject);
                logger.debug("Added SceneObject[{}]: {}", name , polygonalSceneObject);
            }
        } catch (XPathExpressionException e) {
            logger.error("Wrong XPath expression:", e);
        }
        logger.info("SceneObjects have been parsed");
        return sceneObjectSet;
    }

    /**
     * Parsing points list
     * @return Map<Integer, Point> where Integer - point/id
     */
    private Map<Integer, Point> getPoints() {
        Map<Integer, Point> pointMap = new HashMap<>();
        try {
            XPathExpression xPathExpression = xpath.compile("/scene/points/point");
            NodeList points = (NodeList) xPathExpression.evaluate(document, XPathConstants.NODESET);
            logger.trace("Points NodeList size: {}", points.getLength());
            for (int pointsIndex = 0; pointsIndex < points.getLength(); pointsIndex++) {
                Node pointNode = points.item(pointsIndex);
                pointNode.getParentNode().removeChild(pointNode);
                int id = ((Number)xpath.evaluate("id", pointNode, XPathConstants.NUMBER)).intValue();
                double x = ((Number)xpath.evaluate("x", pointNode, XPathConstants.NUMBER)).doubleValue();
                double y = ((Number)xpath.evaluate("y", pointNode, XPathConstants.NUMBER)).doubleValue();
                double z = ((Number)xpath.evaluate("z", pointNode, XPathConstants.NUMBER)).doubleValue();
                logger.trace("Point {}: ID={}, x={}, y={}, z={}", pointsIndex, id, x, y, z);
                Point point = new Point(x, y, z);
                pointMap.put(id, point);
                logger.debug("Added Point[{}]: {}", id, point);
            }
        } catch (XPathExpressionException e) {
            logger.error("Wrong XPath expression:", e);
        }
        logger.info("Points have been parsed");
        return pointMap;
    }

    /**
     * Parsing faces list
     * @return Map<Integer, Face> where Integer - face/id
     */
    private Map<Integer, Face> getFaces() {
        Map<Integer, Face> faceMap = new HashMap<>();
        Map<Integer, Point> pointMap = getPoints();
        try {
            XPathExpression xPathExpression = xpath.compile("/scene/faces/face");
            NodeList faces = (NodeList) xPathExpression.evaluate(document, XPathConstants.NODESET);
            logger.trace("Faces NodeList size: {}", faces.getLength());
            for (int facesIndex = 0; facesIndex < faces.getLength(); facesIndex++) {
                Node faceNode = faces.item(facesIndex);
                faceNode.getParentNode().removeChild(faceNode);
                int id = ((Number)xpath.evaluate("id", faceNode, XPathConstants.NUMBER)).intValue();
                int pointsCount = ((Number)xpath.evaluate("count(point-id)", faceNode, XPathConstants.NUMBER)).intValue();
                Set<Point> pointSet = new HashSet<>();
                if (pointsCount == 3) {
                    for(int pointsIndex = 1; pointsIndex <= pointsCount; pointsIndex++) {
                        pointSet.add(pointMap.get(((Number)xpath.evaluate("(point-id)[" + pointsIndex + "]",
                                faceNode, XPathConstants.NUMBER)).intValue()));
                    }
                }
                Face face = new Face(pointSet);
                faceMap.put(id, face);
                logger.debug("Added Face[{}]: {}", id, face);
            }
        } catch (XPathExpressionException e) {
            logger.error("Wrong XPath expression:", e);
        }
        logger.info("Faces have been parsed");
        return faceMap;
    }

    /**
     * Parsing polygons. In our XML all polygons are faces.
     * @return Map<Integer, Polygon> where Integer - face/id
     */
    private Map<Integer, Polygon> getPolygons() {
        Map<Integer, Polygon> polygonMap = new HashMap<>();
        Map<Integer, Face> faceMap = getFaces();
        for (Integer key: faceMap.keySet()) {
            Polygon polygon = new Polygon(faceMap.get(key));
            polygonMap.put(key, polygon);
            logger.debug("Added Polygon[{}]: {}", key, polygon);
        }
        return polygonMap;
    }

}
TOP

Related Classes of org.kalimullin.fsraytracer.data.SceneDataXpathParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.