parser partial refactor
This commit is contained in:
parent
a782ec5ad7
commit
52d98a4ece
16 changed files with 403 additions and 190 deletions
|
@ -142,7 +142,7 @@ public class Zork {
|
||||||
for (String action : tempTrigger.action) {
|
for (String action : tempTrigger.action) {
|
||||||
d.dispatch(action);
|
d.dispatch(action);
|
||||||
}
|
}
|
||||||
skip = skip || tempTrigger.hasCommand;
|
skip = skip || tempTrigger.hasCommand();
|
||||||
if (tempTrigger.type.equals("single")) {
|
if (tempTrigger.type.equals("single")) {
|
||||||
zorkObject.trigger.remove(x);
|
zorkObject.trigger.remove(x);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
package com.github.dtschust.zork;
|
package com.github.dtschust.zork;
|
||||||
|
|
||||||
/* Special Command condition */
|
/* Special Command condition */
|
||||||
public class ZorkCommand implements ZorkEvaluatable {
|
public class ZorkCommand {
|
||||||
public final String command;
|
public final String commandName;
|
||||||
|
|
||||||
public ZorkCommand(String command) {
|
public ZorkCommand(String command) {
|
||||||
this.command = command;
|
this.commandName = command;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public boolean matchesInput(String userInput) {
|
||||||
public boolean evaluate(Zork zork) {
|
return commandName.equals(userInput);
|
||||||
return command.equals(zork.userInput);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package com.github.dtschust.zork;
|
||||||
import com.github.dtschust.zork.parser.ZorkGame;
|
import com.github.dtschust.zork.parser.ZorkGame;
|
||||||
|
|
||||||
/* Generic condition*/
|
/* Generic condition*/
|
||||||
public abstract class ZorkCondition implements ZorkEvaluatable {
|
public abstract class ZorkCondition {
|
||||||
public final String object;
|
public final String object;
|
||||||
|
|
||||||
protected ZorkCondition(String object) {
|
protected ZorkCondition(String object) {
|
||||||
|
@ -11,9 +11,4 @@ public abstract class ZorkCondition implements ZorkEvaluatable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract boolean evaluate(ZorkGame game);
|
public abstract boolean evaluate(ZorkGame game);
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean evaluate(Zork zork) {
|
|
||||||
return this.evaluate(zork.game);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ public class ZorkConditionHas extends ZorkCondition {
|
||||||
else {
|
else {
|
||||||
ZorkContainer containerObject = (ZorkContainer) game.get("container", owner);
|
ZorkContainer containerObject = (ZorkContainer) game.get("container", owner);
|
||||||
if (containerObject != null) {
|
if (containerObject != null) {
|
||||||
return evaluateCondition(containerObject.item.contains(object));
|
return evaluateCondition(containerObject.containsItem(object));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
package com.github.dtschust.zork;
|
|
||||||
|
|
||||||
public interface ZorkEvaluatable {
|
|
||||||
boolean evaluate(Zork zork);
|
|
||||||
}
|
|
|
@ -1,26 +1,36 @@
|
||||||
package com.github.dtschust.zork;
|
package com.github.dtschust.zork;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/*Trigger*/
|
/*Trigger*/
|
||||||
public class ZorkTrigger {
|
public class ZorkTrigger {
|
||||||
public final List<ZorkEvaluatable> conditions = new ArrayList<>();
|
private final List<ZorkCondition> conditions;
|
||||||
public final List<String> print = new ArrayList<>();
|
private final List<ZorkCommand> commands;
|
||||||
public final List<String> action = new ArrayList<>();
|
public final List<String> print;
|
||||||
public final String type; /*By default, single*/
|
public final List<String> action;
|
||||||
public boolean hasCommand = false;
|
/* By default, "single" */
|
||||||
|
public final String type;
|
||||||
|
|
||||||
public ZorkTrigger(String type) {
|
public boolean hasCommand() {
|
||||||
|
return !this.commands.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ZorkTrigger(final String type,
|
||||||
|
final List<ZorkCondition> conditions,
|
||||||
|
final List<ZorkCommand> commands,
|
||||||
|
final List<String> print,
|
||||||
|
final List<String> action) {
|
||||||
|
this.conditions = conditions;
|
||||||
|
this.commands = commands;
|
||||||
|
this.print = print;
|
||||||
|
this.action = action;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean evaluate(Zork zork) {
|
public boolean evaluate(Zork zork) {
|
||||||
for (ZorkEvaluatable condition : conditions) {
|
if (!commands.stream().allMatch(c -> c.matchesInput(zork.userInput))) {
|
||||||
if (!condition.evaluate(zork)) {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return conditions.stream().allMatch(c -> c.evaluate(zork.game));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
88
src/main/java/com/github/dtschust/zork/parser/DOMUtils.java
Normal file
88
src/main/java/com/github/dtschust/zork/parser/DOMUtils.java
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
package com.github.dtschust.zork.parser;
|
||||||
|
|
||||||
|
import org.w3c.dom.CharacterData;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public final class DOMUtils {
|
||||||
|
private DOMUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a DOM element with one and only one child of text node type, returns the text as a string. If there is no
|
||||||
|
* such node, '?' is returned
|
||||||
|
* Get a string from an element (XML parsing stuff)
|
||||||
|
*
|
||||||
|
* @param e the element
|
||||||
|
* @return the text as string, or '?'
|
||||||
|
*/
|
||||||
|
public static String getInnerText(final Element e) {
|
||||||
|
final Node child = e.getFirstChild();
|
||||||
|
return child instanceof CharacterData ? ((CharacterData) child).getData() : "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getInnerTextByTagName(final Element element, final String elementName) {
|
||||||
|
return getInnerTextByTagName(element, elementName, Optional.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getInnerTextByTagName(final Element element,
|
||||||
|
final String name,
|
||||||
|
final String defaultValue) {
|
||||||
|
return getInnerTextByTagName(element, name, Optional.of(defaultValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getInnerTextByTagName(final Element element,
|
||||||
|
final String elementName,
|
||||||
|
final Optional<String> defaultValue) {
|
||||||
|
final NodeList field = element.getElementsByTagName(elementName);
|
||||||
|
|
||||||
|
if (field.getLength() == 0) {
|
||||||
|
return defaultValue.orElseThrow(() ->
|
||||||
|
new IllegalArgumentException(elementName + " element count in container is not 1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
final Node first = field.item(0);
|
||||||
|
if (!(first instanceof Element)) {
|
||||||
|
// the contract of getElementsByTagName states that it returns a list of Element objects
|
||||||
|
throw new IllegalStateException("unreachable");
|
||||||
|
}
|
||||||
|
|
||||||
|
return getInnerText((Element) first);
|
||||||
|
}
|
||||||
|
|
||||||
|
// the isInstance check assures the cast to T is safe, otherwise IllegalStateException is thrown
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <T> Iterable<T> iterable(final NodeList nodeList, Class<T> type) {
|
||||||
|
return () -> new Iterator<>() {
|
||||||
|
private int index = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return index < nodeList.getLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T next() {
|
||||||
|
if (!hasNext())
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
final Node next = nodeList.item(index++);
|
||||||
|
if (next == null || !type.isAssignableFrom(next.getClass())) {
|
||||||
|
throw new IllegalStateException("Element in list is not of type " + type.getCanonicalName());
|
||||||
|
}
|
||||||
|
return (T) next;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static Iterable<Node> iterNodes(final NodeList nodeList) {
|
||||||
|
return iterable(nodeList, Node.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Iterable<Element> iterator(final NodeList nodeList) {
|
||||||
|
return iterable(nodeList, Element.class);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,15 +1,24 @@
|
||||||
package com.github.dtschust.zork.parser;
|
package com.github.dtschust.zork.parser;
|
||||||
|
|
||||||
import com.github.dtschust.zork.*;
|
import com.github.dtschust.zork.ZorkCondition;
|
||||||
import com.github.dtschust.zork.types.*;
|
import com.github.dtschust.zork.ZorkTrigger;
|
||||||
import org.w3c.dom.CharacterData;
|
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.Element;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
import javax.xml.parsers.DocumentBuilderFactory;
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.*;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class ZorkReader {
|
public class ZorkReader {
|
||||||
|
|
||||||
|
@ -19,21 +28,6 @@ public class ZorkReader {
|
||||||
this.filename = 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) {
|
private static void addCreature(ZorkGame data, Element element) {
|
||||||
final Set<String> vulnerabilities = new HashSet<>();
|
final Set<String> vulnerabilities = new HashSet<>();
|
||||||
final List<ZorkCondition> conditions = new ArrayList<>();
|
final List<ZorkCondition> conditions = new ArrayList<>();
|
||||||
|
@ -42,60 +36,47 @@ public class ZorkReader {
|
||||||
|
|
||||||
|
|
||||||
NodeList attacks = element.getElementsByTagName("attack");
|
NodeList attacks = element.getElementsByTagName("attack");
|
||||||
for (Element attack : iterElements(attacks)) {
|
for (Element attack : DOMUtils.iterator(attacks)) {
|
||||||
|
final List<ZorkCondition> conditionsList = Elements.byTagName(attack, "condition").stream()
|
||||||
|
.map(Parsers.condition::parse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
conditions.addAll(conditionsList);
|
||||||
|
|
||||||
readConditionsInTrigger(attack, conditions);
|
for (Element print : DOMUtils.iterator(attack.getElementsByTagName("print"))) {
|
||||||
|
prints.add(DOMUtils.getInnerText(print));
|
||||||
for (Element print : iterElements(attack.getElementsByTagName("print"))) {
|
|
||||||
prints.add(getString(print));
|
|
||||||
}
|
}
|
||||||
for (Element action : iterElements(attack.getElementsByTagName("action"))) {
|
for (Element action : DOMUtils.iterator(attack.getElementsByTagName("action"))) {
|
||||||
actions.add(getString(action));
|
actions.add(DOMUtils.getInnerText(action));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Element vuln : iterElements(element.getElementsByTagName("vulnerability"))) {
|
for (Element vuln : DOMUtils.iterator(element.getElementsByTagName("vulnerability"))) {
|
||||||
vulnerabilities.add(getString(vuln));
|
vulnerabilities.add(DOMUtils.getInnerText(vuln));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get all possible creature attributes */
|
/* Get all possible creature attributes */
|
||||||
ZorkCreature tempCreature = new ZorkCreature(
|
ZorkCreature tempCreature = new ZorkCreature(
|
||||||
getItemOrDefault(element, "name", ""),
|
DOMUtils.getInnerTextByTagName(element, "name", ""),
|
||||||
getItemOrDefault(element, "description", ""),
|
DOMUtils.getInnerTextByTagName(element, "description", ""),
|
||||||
vulnerabilities,
|
vulnerabilities,
|
||||||
conditions,
|
conditions,
|
||||||
prints,
|
prints,
|
||||||
actions
|
actions
|
||||||
);
|
);
|
||||||
|
|
||||||
readTriggersInObject(element, tempCreature);
|
final List<ZorkTrigger> triggers = Elements.byTagName(element, "trigger").stream()
|
||||||
tempCreature.updateStatus(getItemOrDefault(element, "status", ""));
|
.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*/
|
/* Put each creature in the creatures hashmap, the generic object hashmap, and the objectlookup hashmap*/
|
||||||
data.addObjectThroughLookup("creature", tempCreature);
|
data.addObjectThroughLookup("creature", tempCreature);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addContainer(ZorkGame data, Element element) {
|
private static void addContainer(ZorkGame data, Element element) {
|
||||||
/*Get all possible container attributes*/
|
final ZorkContainer tempCont = Parsers.container.parse(element);
|
||||||
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*/
|
/* Put each container in the containers hashmap, the generic object hashmap, and the objectlookup hashmap*/
|
||||||
data.addObjectThroughLookup("container", tempCont);
|
data.addObjectThroughLookup("container", tempCont);
|
||||||
|
@ -107,139 +88,74 @@ public class ZorkReader {
|
||||||
|
|
||||||
NodeList turnOn = element.getElementsByTagName("turnon");
|
NodeList turnOn = element.getElementsByTagName("turnon");
|
||||||
if (turnOn.getLength() > 0) {
|
if (turnOn.getLength() > 0) {
|
||||||
for (Element print : iterElements(element.getElementsByTagName("print"))) {
|
for (Element print : DOMUtils.iterator(element.getElementsByTagName("print"))) {
|
||||||
prints.add(getString(print));
|
prints.add(DOMUtils.getInnerText(print));
|
||||||
}
|
}
|
||||||
for (Element action : iterElements(element.getElementsByTagName("action"))) {
|
for (Element action : DOMUtils.iterator(element.getElementsByTagName("action"))) {
|
||||||
actions.add(getString(action));
|
actions.add(DOMUtils.getInnerText(action));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get all possible item attributes*/
|
/* Get all possible item attributes*/
|
||||||
ZorkItem tempItem = new ZorkItem(
|
ZorkItem tempItem = new ZorkItem(
|
||||||
getItemOrDefault(element, "name", ""),
|
DOMUtils.getInnerTextByTagName(element, "name", ""),
|
||||||
getItemOrDefault(element, "description", ""),
|
DOMUtils.getInnerTextByTagName(element, "description", ""),
|
||||||
getItemOrDefault(element, "writing", ""),
|
DOMUtils.getInnerTextByTagName(element, "writing", ""),
|
||||||
prints,
|
prints,
|
||||||
actions
|
actions
|
||||||
);
|
);
|
||||||
|
|
||||||
readTriggersInObject(element, tempItem);
|
final List<ZorkTrigger> 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 */
|
/* Put each item in the items hashmap, the generic objects hashmap, and store its type in object lookup */
|
||||||
data.addObjectThroughLookup("item", tempItem);
|
data.addObjectThroughLookup("item", tempItem);
|
||||||
tempItem.updateStatus(getItemOrDefault(element, "status", ""));
|
tempItem.updateStatus(DOMUtils.getInnerTextByTagName(element, "status", ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void addRoom(ZorkGame data, Element element) {
|
private static void addRoom(ZorkGame data, Element element) {
|
||||||
|
|
||||||
/*Get all possible Room attributes*/
|
/*Get all possible Room attributes*/
|
||||||
ZorkRoom tempRoom = new ZorkRoom(
|
ZorkRoom tempRoom = new ZorkRoom(
|
||||||
getItemOrDefault(element, "name", ""),
|
DOMUtils.getInnerTextByTagName(element, "name", ""),
|
||||||
getItemOrDefault(element, "description", ""),
|
DOMUtils.getInnerTextByTagName(element, "description", ""),
|
||||||
getItemOrDefault(element, "type", "regular")
|
DOMUtils.getInnerTextByTagName(element, "type", "regular")
|
||||||
);
|
);
|
||||||
|
|
||||||
readTriggersInObject(element, tempRoom);
|
final List<ZorkTrigger> triggers = Elements.byTagName(element, "trigger").stream()
|
||||||
tempRoom.updateStatus(getItemOrDefault(element, "status", ""));
|
.map(Parsers.trigger::parse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
tempRoom.trigger.addAll(triggers);
|
||||||
|
|
||||||
for (Element item : iterElements(element.getElementsByTagName("item"))) {
|
tempRoom.updateStatus(DOMUtils.getInnerTextByTagName(element, "status", ""));
|
||||||
String itemName = getString(item);
|
|
||||||
|
for (Element item : DOMUtils.iterator(element.getElementsByTagName("item"))) {
|
||||||
|
String itemName = DOMUtils.getInnerText(item);
|
||||||
tempRoom.item.add(itemName);
|
tempRoom.item.add(itemName);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Element creature : iterElements(element.getElementsByTagName("creature"))) {
|
for (Element creature : DOMUtils.iterator(element.getElementsByTagName("creature"))) {
|
||||||
String creatureName = getString(creature);
|
String creatureName = DOMUtils.getInnerText(creature);
|
||||||
tempRoom.creature.add(creatureName);
|
tempRoom.creature.add(creatureName);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Element container : iterElements(element.getElementsByTagName("container"))) {
|
for (Element container : DOMUtils.iterator(element.getElementsByTagName("container"))) {
|
||||||
String containerName = getString(container);
|
String containerName = DOMUtils.getInnerText(container);
|
||||||
tempRoom.container.add(containerName);
|
tempRoom.container.add(containerName);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Element border : iterElements(element.getElementsByTagName("border"))) {
|
for (Element border : DOMUtils.iterator(element.getElementsByTagName("border"))) {
|
||||||
String borderDirection = getString((Element) border.getElementsByTagName("direction").item(0));
|
String borderDirection = DOMUtils.getInnerText((Element) border.getElementsByTagName("direction").item(0));
|
||||||
String borderName = getString((Element) border.getElementsByTagName("name").item(0));
|
String borderName = DOMUtils.getInnerText((Element) border.getElementsByTagName("name").item(0));
|
||||||
tempRoom.border.put(borderDirection, borderName);
|
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*/
|
/*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);
|
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() {
|
public ZorkGame build() {
|
||||||
|
|
||||||
ZorkGame data = new ZorkGame();
|
ZorkGame data = new ZorkGame();
|
||||||
|
|
||||||
File file = new File(filename);
|
File file = new File(filename);
|
||||||
|
@ -259,7 +175,7 @@ public class ZorkReader {
|
||||||
Element rootElement = builder.newDocumentBuilder().parse(file).getDocumentElement();
|
Element rootElement = builder.newDocumentBuilder().parse(file).getDocumentElement();
|
||||||
|
|
||||||
/* Every single first generation child is a room, container, creature, or item. So load them in*/
|
/* Every single first generation child is a room, container, creature, or item. So load them in*/
|
||||||
for (Node node : iterNodes(rootElement.getChildNodes())) {
|
for (Node node : DOMUtils.iterNodes(rootElement.getChildNodes())) {
|
||||||
Element element;
|
Element element;
|
||||||
if (node instanceof Element) {
|
if (node instanceof Element) {
|
||||||
element = (Element) node;
|
element = (Element) node;
|
||||||
|
@ -287,6 +203,7 @@ public class ZorkReader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
System.out.println("Invalid XML file, exiting");
|
System.out.println("Invalid XML file, exiting");
|
||||||
System.exit(-1);
|
System.exit(-1);
|
||||||
//e.printStackTrace();
|
//e.printStackTrace();
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
package com.github.dtschust.zork.parser.builders;
|
||||||
|
|
||||||
|
import com.github.dtschust.zork.ZorkCondition;
|
||||||
|
import com.github.dtschust.zork.ZorkTrigger;
|
||||||
|
import com.github.dtschust.zork.types.ZorkContainer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inversion of control for Zork parse strategies
|
||||||
|
*/
|
||||||
|
public final class Parsers {
|
||||||
|
public static final ZorkParseStrategy<ZorkCondition> condition = new ZorkConditionParseStrategy();
|
||||||
|
public static final ZorkParseStrategy<ZorkTrigger> trigger = new ZorkTriggerListParseStrategy(condition);
|
||||||
|
public static final ZorkParseStrategy<ZorkContainer> container = new ZorkContainerParseStrategy(trigger);
|
||||||
|
|
||||||
|
private Parsers() {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.github.dtschust.zork.parser.builders;
|
||||||
|
|
||||||
|
import com.github.dtschust.zork.ZorkCondition;
|
||||||
|
import com.github.dtschust.zork.ZorkConditionHas;
|
||||||
|
import com.github.dtschust.zork.ZorkConditionStatus;
|
||||||
|
import com.github.dtschust.zork.parser.DOMUtils;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
|
public class ZorkConditionParseStrategy implements ZorkParseStrategy<ZorkCondition> {
|
||||||
|
@Override
|
||||||
|
public ZorkCondition parse(final Element conditionElement) {
|
||||||
|
if (conditionElement.getElementsByTagName("has").getLength() > 0) {
|
||||||
|
return new ZorkConditionHas(
|
||||||
|
DOMUtils.getInnerTextByTagName(conditionElement, "has"),
|
||||||
|
DOMUtils.getInnerTextByTagName(conditionElement, "object"),
|
||||||
|
DOMUtils.getInnerTextByTagName(conditionElement, "owner")
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return new ZorkConditionStatus(
|
||||||
|
DOMUtils.getInnerTextByTagName(conditionElement, "status"),
|
||||||
|
DOMUtils.getInnerTextByTagName(conditionElement, "object")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
package com.github.dtschust.zork.parser.builders;
|
||||||
|
|
||||||
|
import com.github.dtschust.zork.ZorkTrigger;
|
||||||
|
import com.github.dtschust.zork.parser.DOMUtils;
|
||||||
|
import com.github.dtschust.zork.parser.dom.Elements;
|
||||||
|
import com.github.dtschust.zork.types.ZorkContainer;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ZorkContainerParseStrategy implements ZorkParseStrategy<ZorkContainer> {
|
||||||
|
private final ZorkParseStrategy<ZorkTrigger> triggerStrategy;
|
||||||
|
|
||||||
|
public ZorkContainerParseStrategy(ZorkParseStrategy<ZorkTrigger> triggerStrategy) {
|
||||||
|
this.triggerStrategy = triggerStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ZorkContainer parse(final Element element) {
|
||||||
|
final String name = DOMUtils.getInnerTextByTagName(element, "name", "");
|
||||||
|
|
||||||
|
final String description = DOMUtils.getInnerTextByTagName(element, "description", "");
|
||||||
|
|
||||||
|
final List<ZorkTrigger> triggers = Elements.byTagName(element, "trigger").stream()
|
||||||
|
.map(triggerStrategy::parse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
final String status = DOMUtils.getInnerTextByTagName(element, "status", "");
|
||||||
|
|
||||||
|
final List<String> accepts = Elements.innerTextByTagName(element, "accept");
|
||||||
|
|
||||||
|
// If a container has an accepts attribute, then it is always open
|
||||||
|
final boolean open = !accepts.isEmpty();
|
||||||
|
|
||||||
|
final List<String> items = Elements.innerTextByTagName(element, "item");
|
||||||
|
|
||||||
|
return new ZorkContainer(name, description, open, status, items, accepts, triggers);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package com.github.dtschust.zork.parser.builders;
|
||||||
|
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
|
public interface ZorkParseStrategy<T> {
|
||||||
|
T parse(Element source);
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package com.github.dtschust.zork.parser.builders;
|
||||||
|
|
||||||
|
import com.github.dtschust.zork.ZorkCommand;
|
||||||
|
import com.github.dtschust.zork.ZorkCondition;
|
||||||
|
import com.github.dtschust.zork.ZorkTrigger;
|
||||||
|
import com.github.dtschust.zork.parser.DOMUtils;
|
||||||
|
import com.github.dtschust.zork.parser.dom.Elements;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class ZorkTriggerListParseStrategy implements ZorkParseStrategy<ZorkTrigger> {
|
||||||
|
|
||||||
|
private final ZorkParseStrategy<ZorkCondition> conditionStrategy;
|
||||||
|
|
||||||
|
public ZorkTriggerListParseStrategy(final ZorkParseStrategy<ZorkCondition> conditionStrategy) {
|
||||||
|
this.conditionStrategy = conditionStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ZorkTrigger parse(final Element trigger) {
|
||||||
|
final String type = DOMUtils.getInnerTextByTagName((Element) trigger.getParentNode(), "type", "single");
|
||||||
|
|
||||||
|
final List<ZorkCommand> commands = Elements.byTagName(trigger, "command").stream()
|
||||||
|
.map(DOMUtils::getInnerText)
|
||||||
|
.map(ZorkCommand::new)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
final boolean hasCommand = !commands.isEmpty();
|
||||||
|
|
||||||
|
final List<ZorkCondition> conditions = Elements.byTagName(trigger, "condition").stream()
|
||||||
|
.map(conditionStrategy::parse)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
final List<String> prints = Elements.innerTextByTagName(trigger, "print");
|
||||||
|
final List<String> actions = Elements.innerTextByTagName(trigger, "action");
|
||||||
|
|
||||||
|
return new ZorkTrigger(type, conditions, commands, prints, actions);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package com.github.dtschust.zork.parser.dom;
|
||||||
|
|
||||||
|
import com.github.dtschust.zork.parser.DOMUtils;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
|
||||||
|
import java.util.AbstractList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.RandomAccess;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class Elements extends AbstractList<Element> implements RandomAccess {
|
||||||
|
private final NodeList list;
|
||||||
|
|
||||||
|
public static Elements byTagName(final Element parent, final String name) {
|
||||||
|
return new Elements(parent.getElementsByTagName(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> innerTextByTagName(final Element parent, final String name) {
|
||||||
|
return Elements.byTagName(parent, name).stream()
|
||||||
|
.map(DOMUtils::getInnerText)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Elements(final NodeList l) {
|
||||||
|
list = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Element get(int index) {
|
||||||
|
final Node e = list.item(index);
|
||||||
|
if (!(e instanceof Element)) {
|
||||||
|
// the contract of getElementsByTagName states that it returns a list of Element objects
|
||||||
|
throw new IllegalStateException("unreachable");
|
||||||
|
}
|
||||||
|
return (Element) e;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return list.getLength();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,20 +1,43 @@
|
||||||
package com.github.dtschust.zork.types;
|
package com.github.dtschust.zork.types;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import com.github.dtschust.zork.ZorkTrigger;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/* Container*/
|
/* Container*/
|
||||||
public class ZorkContainer extends ZorkObject {
|
public class ZorkContainer extends ZorkObject {
|
||||||
public final Set<String> item = new HashSet<>();
|
public final Set<String> item;
|
||||||
public final List<String> accept = new ArrayList<>();
|
private final List<String> accepts;
|
||||||
protected boolean open = false;
|
private boolean open;
|
||||||
|
|
||||||
public ZorkContainer(String name, String description) {
|
public ZorkContainer(final String name,
|
||||||
super(name, description);
|
final String description,
|
||||||
|
final boolean open,
|
||||||
|
final String status,
|
||||||
|
final Collection<String> items,
|
||||||
|
final Collection<String> accepts,
|
||||||
|
final Collection<ZorkTrigger> triggers) {
|
||||||
|
super(name, description, status, triggers);
|
||||||
|
this.open = open;
|
||||||
|
this.item = new HashSet<>(items);
|
||||||
|
this.accepts = new ArrayList<>(accepts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addItem(final String item) {
|
||||||
|
this.item.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeItem(final String item) {
|
||||||
|
this.item.remove(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsItem(final String item) {
|
||||||
|
return this.item.contains(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return this.item.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isOpen() {
|
public boolean isOpen() {
|
||||||
return open;
|
return open;
|
||||||
|
@ -23,5 +46,4 @@ public class ZorkContainer extends ZorkObject {
|
||||||
public void open() {
|
public void open() {
|
||||||
open = true;
|
open = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,20 +3,32 @@ package com.github.dtschust.zork.types;
|
||||||
import com.github.dtschust.zork.ZorkTrigger;
|
import com.github.dtschust.zork.ZorkTrigger;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/* Generic object, everything inherits from this*/
|
/* Generic object, everything inherits from this*/
|
||||||
public abstract class ZorkObject {
|
public abstract class ZorkObject {
|
||||||
public final String name;
|
public final String name;
|
||||||
public final String description;
|
public final String description;
|
||||||
public final List<ZorkTrigger> trigger = new ArrayList<>();
|
public final List<ZorkTrigger> trigger;
|
||||||
private String status;
|
private String status;
|
||||||
|
|
||||||
protected ZorkObject(String name, String description) {
|
protected ZorkObject(String name, String description) {
|
||||||
|
this(name, description, null, Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ZorkObject(final String name,
|
||||||
|
final String description,
|
||||||
|
final String status,
|
||||||
|
final Collection<ZorkTrigger> triggers) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
|
this.status = status;
|
||||||
|
this.trigger = new ArrayList<>(triggers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void updateStatus(String status) {
|
public void updateStatus(String status) {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue