package com.github.dtschust.zork.parser; import com.github.dtschust.zork.ZorkCondition; import com.github.dtschust.zork.ZorkTrigger; import com.github.dtschust.zork.parser.builders.Parsers; import com.github.dtschust.zork.parser.dom.Elements; import com.github.dtschust.zork.types.ZorkContainer; import com.github.dtschust.zork.types.ZorkCreature; import com.github.dtschust.zork.types.ZorkItem; import com.github.dtschust.zork.types.ZorkRoom; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.xml.parsers.DocumentBuilderFactory; import java.io.File; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class ZorkReader { private final String filename; public ZorkReader(String filename) { this.filename = filename; } private static void addCreature(ZorkGame data, Element element) { final Set vulnerabilities = new HashSet<>(); final List conditions = new ArrayList<>(); final List prints = new ArrayList<>(); final List actions = new ArrayList<>(); NodeList attacks = element.getElementsByTagName("attack"); for (Element attack : DOMUtils.iterator(attacks)) { final List conditionsList = Elements.byTagName(attack, "condition").stream() .map(Parsers.condition::parse) .collect(Collectors.toList()); conditions.addAll(conditionsList); for (Element print : DOMUtils.iterator(attack.getElementsByTagName("print"))) { prints.add(DOMUtils.getInnerText(print)); } for (Element action : DOMUtils.iterator(attack.getElementsByTagName("action"))) { actions.add(DOMUtils.getInnerText(action)); } } for (Element vuln : DOMUtils.iterator(element.getElementsByTagName("vulnerability"))) { vulnerabilities.add(DOMUtils.getInnerText(vuln)); } /* Get all possible creature attributes */ ZorkCreature tempCreature = new ZorkCreature( DOMUtils.getInnerTextByTagName(element, "name", ""), DOMUtils.getInnerTextByTagName(element, "description", ""), vulnerabilities, conditions, prints, actions ); final List triggers = Elements.byTagName(element, "trigger").stream() .map(Parsers.trigger::parse) .collect(Collectors.toList()); tempCreature.trigger.addAll(triggers); tempCreature.updateStatus(DOMUtils.getInnerTextByTagName(element, "status", "")); /* Put each creature in the creatures hashmap, the generic object hashmap, and the objectlookup hashmap*/ data.addObjectThroughLookup("creature", tempCreature); } private static void addContainer(ZorkGame data, Element element) { final ZorkContainer tempCont = Parsers.container.parse(element); /* Put each container in the containers hashmap, the generic object hashmap, and the objectlookup hashmap*/ data.addObjectThroughLookup("container", tempCont); } private static void addItem(ZorkGame data, Element element) { final List prints = new ArrayList<>(); final List actions = new ArrayList<>(); NodeList turnOn = element.getElementsByTagName("turnon"); if (turnOn.getLength() > 0) { for (Element print : DOMUtils.iterator(element.getElementsByTagName("print"))) { prints.add(DOMUtils.getInnerText(print)); } for (Element action : DOMUtils.iterator(element.getElementsByTagName("action"))) { actions.add(DOMUtils.getInnerText(action)); } } /* Get all possible item attributes*/ ZorkItem tempItem = new ZorkItem( DOMUtils.getInnerTextByTagName(element, "name", ""), DOMUtils.getInnerTextByTagName(element, "description", ""), DOMUtils.getInnerTextByTagName(element, "writing", ""), prints, actions ); final List triggers = Elements.byTagName(element, "trigger").stream() .map(Parsers.trigger::parse) .collect(Collectors.toList()); tempItem.trigger.addAll(triggers); /* Put each item in the items hashmap, the generic objects hashmap, and store its type in object lookup */ data.addObjectThroughLookup("item", tempItem); tempItem.updateStatus(DOMUtils.getInnerTextByTagName(element, "status", "")); } private static void addRoom(ZorkGame data, Element element) { /*Get all possible Room attributes*/ ZorkRoom tempRoom = new ZorkRoom( DOMUtils.getInnerTextByTagName(element, "name", ""), DOMUtils.getInnerTextByTagName(element, "description", ""), DOMUtils.getInnerTextByTagName(element, "type", "regular") ); final List triggers = Elements.byTagName(element, "trigger").stream() .map(Parsers.trigger::parse) .collect(Collectors.toList()); tempRoom.trigger.addAll(triggers); tempRoom.updateStatus(DOMUtils.getInnerTextByTagName(element, "status", "")); for (Element item : DOMUtils.iterator(element.getElementsByTagName("item"))) { String itemName = DOMUtils.getInnerText(item); tempRoom.item.add(itemName); } for (Element creature : DOMUtils.iterator(element.getElementsByTagName("creature"))) { String creatureName = DOMUtils.getInnerText(creature); tempRoom.creature.add(creatureName); } for (Element container : DOMUtils.iterator(element.getElementsByTagName("container"))) { String containerName = DOMUtils.getInnerText(container); tempRoom.container.add(containerName); } for (Element border : DOMUtils.iterator(element.getElementsByTagName("border"))) { String borderDirection = DOMUtils.getInnerText((Element) border.getElementsByTagName("direction").item(0)); String borderName = DOMUtils.getInnerText((Element) border.getElementsByTagName("name").item(0)); tempRoom.border.put(borderDirection, borderName); } /*Add this room to the rooms hashmap, put it in the generic objects hashmap, and store it's type in the objectlookup hashmap*/ data.addObjectThroughLookup("room", tempRoom); } public ZorkGame build() { ZorkGame data = new ZorkGame(); File file = new File(filename); if (!file.canRead()) { System.out.println("Error opening file. Exiting..."); throw new RuntimeException(); } try { /* Open the xml file*/ DocumentBuilderFactory builder = DocumentBuilderFactory.newInstance(); // Limit XML features to mitigate vulnerabilities builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); builder.setFeature("http://xml.org/sax/features/external-general-entities", false); builder.setFeature("http://xml.org/sax/features/external-parameter-entities", false); Element rootElement = builder.newDocumentBuilder().parse(file).getDocumentElement(); /* Every single first generation child is a room, container, creature, or item. So load them in*/ for (Node node : DOMUtils.iterNodes(rootElement.getChildNodes())) { Element element; if (node instanceof Element) { element = (Element) node; String tagType = element.getTagName(); switch (tagType) { /* If it's a room ... */ case "room": addRoom(data, element); break; /* If it's an item... */ case "item": addItem(data, element); break; /* If it's a container... */ case "container": addContainer(data, element); break; /* And finally, if it's a creature...*/ case "creature": addCreature(data, element); break; } } } } catch (Exception e) { e.printStackTrace(); System.out.println("Invalid XML file, exiting"); System.exit(-1); //e.printStackTrace(); } return data; } }