298 lines
12 KiB
Java
298 lines
12 KiB
Java
package com.github.dtschust.zork.parser;
|
|
|
|
import com.github.dtschust.zork.*;
|
|
import com.github.dtschust.zork.types.*;
|
|
import org.w3c.dom.CharacterData;
|
|
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.*;
|
|
|
|
public class ZorkReader {
|
|
|
|
private final String filename;
|
|
|
|
public ZorkReader(String filename) {
|
|
this.filename = filename;
|
|
}
|
|
|
|
/* Get a string from an element (XML parsing stuff)*/
|
|
public static String getString(Element e) {
|
|
Node child = e.getFirstChild();
|
|
if (child instanceof CharacterData) {
|
|
CharacterData cd = (CharacterData) child;
|
|
return cd.getData();
|
|
}
|
|
return "?";
|
|
}
|
|
|
|
private static String getItemOrDefault(Element element, String name, String base) {
|
|
NodeList field = element.getElementsByTagName(name);
|
|
return (field.getLength() > 0) ? getString((Element) field.item(0)) : base;
|
|
}
|
|
|
|
private static void addCreature(ZorkGame data, Element element) {
|
|
final Set<String> vulnerabilities = new HashSet<>();
|
|
final List<ZorkCondition> conditions = new ArrayList<>();
|
|
final List<String> prints = new ArrayList<>();
|
|
final List<String> actions = new ArrayList<>();
|
|
|
|
|
|
NodeList attacks = element.getElementsByTagName("attack");
|
|
for (Element attack : iterElements(attacks)) {
|
|
|
|
readConditionsInTrigger(attack, conditions);
|
|
|
|
for (Element print : iterElements(attack.getElementsByTagName("print"))) {
|
|
prints.add(getString(print));
|
|
}
|
|
for (Element action : iterElements(attack.getElementsByTagName("action"))) {
|
|
actions.add(getString(action));
|
|
}
|
|
}
|
|
|
|
for (Element vuln : iterElements(element.getElementsByTagName("vulnerability"))) {
|
|
vulnerabilities.add(getString(vuln));
|
|
}
|
|
|
|
/* Get all possible creature attributes */
|
|
ZorkCreature tempCreature = new ZorkCreature(
|
|
getItemOrDefault(element, "name", ""),
|
|
getItemOrDefault(element, "description", ""),
|
|
vulnerabilities,
|
|
conditions,
|
|
prints,
|
|
actions
|
|
);
|
|
|
|
readTriggersInObject(element, tempCreature);
|
|
tempCreature.updateStatus(getItemOrDefault(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) {
|
|
/*Get all possible container attributes*/
|
|
ZorkContainer tempCont = new ZorkContainer(
|
|
getItemOrDefault(element, "name", ""),
|
|
getItemOrDefault(element, "description", "")
|
|
);
|
|
|
|
readTriggersInObject(element, tempCont);
|
|
tempCont.updateStatus(getItemOrDefault(element, "status", ""));
|
|
|
|
/*Initially assume a closed container*/
|
|
for (Element accept : iterElements(element.getElementsByTagName("accept"))) {
|
|
/* If container has an accepts attribute, then it is always open*/
|
|
tempCont.open();
|
|
tempCont.accept.add(getString(accept));
|
|
}
|
|
|
|
for (Element item : iterElements(element.getElementsByTagName("item"))) {
|
|
String itemName = getString(item);
|
|
tempCont.item.add(itemName);
|
|
}
|
|
|
|
/* 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<String> prints = new ArrayList<>();
|
|
final List<String> actions = new ArrayList<>();
|
|
|
|
NodeList turnOn = element.getElementsByTagName("turnon");
|
|
if (turnOn.getLength() > 0) {
|
|
for (Element print : iterElements(element.getElementsByTagName("print"))) {
|
|
prints.add(getString(print));
|
|
}
|
|
for (Element action : iterElements(element.getElementsByTagName("action"))) {
|
|
actions.add(getString(action));
|
|
}
|
|
}
|
|
|
|
/* Get all possible item attributes*/
|
|
ZorkItem tempItem = new ZorkItem(
|
|
getItemOrDefault(element, "name", ""),
|
|
getItemOrDefault(element, "description", ""),
|
|
getItemOrDefault(element, "writing", ""),
|
|
prints,
|
|
actions
|
|
);
|
|
|
|
readTriggersInObject(element, tempItem);
|
|
|
|
/* Put each item in the items hashmap, the generic objects hashmap, and store its type in object lookup */
|
|
data.addObjectThroughLookup("item", tempItem);
|
|
tempItem.updateStatus(getItemOrDefault(element, "status", ""));
|
|
}
|
|
|
|
private static void addRoom(ZorkGame data, Element element) {
|
|
|
|
/*Get all possible Room attributes*/
|
|
ZorkRoom tempRoom = new ZorkRoom(
|
|
getItemOrDefault(element, "name", ""),
|
|
getItemOrDefault(element, "description", ""),
|
|
getItemOrDefault(element, "type", "regular")
|
|
);
|
|
|
|
readTriggersInObject(element, tempRoom);
|
|
tempRoom.updateStatus(getItemOrDefault(element, "status", ""));
|
|
|
|
for (Element item : iterElements(element.getElementsByTagName("item"))) {
|
|
String itemName = getString(item);
|
|
tempRoom.item.add(itemName);
|
|
}
|
|
|
|
for (Element creature : iterElements(element.getElementsByTagName("creature"))) {
|
|
String creatureName = getString(creature);
|
|
tempRoom.creature.add(creatureName);
|
|
}
|
|
|
|
for (Element container : iterElements(element.getElementsByTagName("container"))) {
|
|
String containerName = getString(container);
|
|
tempRoom.container.add(containerName);
|
|
}
|
|
|
|
for (Element border : iterElements(element.getElementsByTagName("border"))) {
|
|
String borderDirection = getString((Element) border.getElementsByTagName("direction").item(0));
|
|
String borderName = getString((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);
|
|
}
|
|
|
|
private static void readTriggersInObject(Element element, ZorkObject tempRoom) {
|
|
for (Element trigger : iterElements(element.getElementsByTagName("trigger"))) {
|
|
ZorkTrigger tempTrigger = new ZorkTrigger(getItemOrDefault(element, "type", "single"));
|
|
for (Element command : iterElements(trigger.getElementsByTagName("command"))) {
|
|
ZorkCommand tempCommand = new ZorkCommand(getString(command));
|
|
tempTrigger.conditions.add(tempCommand);
|
|
tempTrigger.hasCommand = true;
|
|
}
|
|
|
|
readConditionsInTrigger(trigger, tempTrigger.conditions);
|
|
|
|
for (Element print : iterElements(trigger.getElementsByTagName("print"))) {
|
|
tempTrigger.print.add(getString(print));
|
|
}
|
|
for (Element action : iterElements(trigger.getElementsByTagName("action"))) {
|
|
tempTrigger.action.add(getString(action));
|
|
}
|
|
|
|
tempRoom.trigger.add(tempTrigger);
|
|
}
|
|
}
|
|
|
|
private static void readConditionsInTrigger(Element trigger, List<? super ZorkCondition> conditionsList) {
|
|
for (Element condition : iterElements(trigger.getElementsByTagName("condition"))) {
|
|
NodeList object = condition.getElementsByTagName("object");
|
|
NodeList has = condition.getElementsByTagName("has");
|
|
if (has.getLength() > 0) {
|
|
NodeList owner = condition.getElementsByTagName("owner");
|
|
ZorkConditionHas tempConditionHas = new ZorkConditionHas(
|
|
getString((Element) has.item(0)),
|
|
getString((Element) object.item(0)),
|
|
getString((Element) owner.item(0))
|
|
);
|
|
conditionsList.add(tempConditionHas);
|
|
} else {
|
|
NodeList sstatus = condition.getElementsByTagName("status");
|
|
ZorkConditionStatus tempConditionStatus = new ZorkConditionStatus(
|
|
getString((Element) sstatus.item(0)),
|
|
getString((Element) object.item(0))
|
|
);
|
|
conditionsList.add(tempConditionStatus);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static <T> Iterable<T> iterable(final NodeList nodeList) {
|
|
return () -> new Iterator<>() {
|
|
private int index = 0;
|
|
|
|
@Override
|
|
public boolean hasNext() {
|
|
return index < nodeList.getLength();
|
|
}
|
|
|
|
@Override
|
|
public T next() {
|
|
if (!hasNext())
|
|
throw new NoSuchElementException();
|
|
return (T) nodeList.item(index++);
|
|
}
|
|
};
|
|
}
|
|
|
|
private static Iterable<Node> iterNodes(final NodeList nodeList) {
|
|
return iterable(nodeList);
|
|
}
|
|
|
|
private static Iterable<Element> iterElements(final NodeList nodeList) {
|
|
return iterable(nodeList);
|
|
}
|
|
|
|
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 : 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) {
|
|
System.out.println("Invalid XML file, exiting");
|
|
System.exit(-1);
|
|
//e.printStackTrace();
|
|
}
|
|
|
|
return data;
|
|
}
|
|
}
|