001    package com.basilv.examples.dom4j;
002    
003    import java.io.File;
004    
005    import org.dom4j.*;
006    import org.dom4j.io.SAXReader;
007    
008    public class MazeXmlConverter
009    {
010      private static final String MAZE_ELEMENT = "maze";
011      private static final String HEIGHT_ATTRIBUTE = "height";
012      private static final String WIDTH_ATTRIBUTE = "width";
013      private static final String WALL_ELEMENT = "wall";
014      private static final String START_X_ATTRIBUTE = "startX";
015      private static final String START_Y_ATTRIBUTE = "startY";
016      private static final String END_X_ATTRIBUTE = "endX";
017      private static final String END_Y_ATTRIBUTE = "endY";
018    
019      public String toXml(Maze maze) {
020        Document document = DocumentHelper.createDocument();
021        Element root = document.addElement(MAZE_ELEMENT)
022          .addAttribute(WIDTH_ATTRIBUTE, Integer.toString(maze.getSize().width))
023          .addAttribute(HEIGHT_ATTRIBUTE, Integer.toString(maze.getSize().height))
024          .addText(nullToEmpty(maze.getDescription()));
025    
026        for (Wall wall : maze.getWalls()) {
027          root.addElement(WALL_ELEMENT)
028            .addAttribute(START_X_ATTRIBUTE, Integer.toString(wall.getStart().x))
029            .addAttribute(START_Y_ATTRIBUTE, Integer.toString(wall.getStart().y))
030            .addAttribute(END_X_ATTRIBUTE, Integer.toString(wall.getEnd().x))
031            .addAttribute(END_Y_ATTRIBUTE, Integer.toString(wall.getEnd().y));
032        }
033    
034        return document.asXML();
035      }
036    
037      private String nullToEmpty(String text) {
038        if (text == null) {
039          return "";
040        } else {
041          return text;
042        }
043      }
044      
045      public Maze fromXml(File xmlFile) {
046        SAXReader reader = new SAXReader();
047        try {
048          Document document = reader.read(xmlFile);
049          return fromXml(document);
050        } catch (DocumentException e) {
051          throw new RuntimeException("Error reading file [" + xmlFile.getAbsolutePath() + "].", e);
052        }
053      }
054    
055      // For testing.
056      public Maze fromXml(String xml) {
057        try {
058          Document document = DocumentHelper.parseText(xml);
059          return fromXml(document);
060        } catch (DocumentException e) {
061          throw new RuntimeException("Error parsing xml string.", e);
062        }
063      }
064      
065      private Maze fromXml(Document document) {
066        Element root = document.getRootElement();
067    
068        int width = getElementIntAttribute(root, WIDTH_ATTRIBUTE);
069        int height = getElementIntAttribute(root, HEIGHT_ATTRIBUTE);
070    
071        Maze maze = new Maze(width, height);
072        maze.setDescription(root.getText());
073    
074        for (Object elementObj : root.elements(WALL_ELEMENT)) {
075          Element element = (Element) elementObj;
076    
077          int startX = getElementIntAttribute(element, START_X_ATTRIBUTE);
078          int startY = getElementIntAttribute(element, START_Y_ATTRIBUTE);
079          int endX = getElementIntAttribute(element, END_X_ATTRIBUTE);
080          int endY = getElementIntAttribute(element, END_Y_ATTRIBUTE);
081    
082          maze.addWall(startX, startY, endX, endY);
083        }
084    
085        return maze;
086      }
087    
088      private int getElementIntAttribute(Element element, String attributeName) {
089        Attribute attribute = getNonNullAttributeForElement(element, attributeName);
090        return Integer.parseInt(attribute.getValue());
091      }
092    
093      private Attribute getNonNullAttributeForElement(Element element, String attributeName) {
094        Attribute attribute = element.attribute(attributeName);
095        if (attribute == null) {
096          throw new RuntimeException("Element [" + element.getName() + "] missing attribute named [" + attributeName + "].");
097        }
098        return attribute;
099      }
100    
101    }