/******************************************************************************* * Copyright 2014 Pawel Pastuszak * <p> * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package pl.kotcrab.jdialogue.parser.impl; import org.jdom2.Document; import org.jdom2.Element; import org.jdom2.JDOMException; import org.jdom2.input.SAXBuilder; import pl.kotcrab.jdialogue.loader.DialogueLoader; import pl.kotcrab.jdialogue.parser.CallbackListener; import pl.kotcrab.jdialogue.parser.ComponentType; import pl.kotcrab.jdialogue.parser.DialogueParser; import pl.kotcrab.jdialogue.parser.DialogueParserException; import pl.kotcrab.jdialogue.parser.PCallback; import pl.kotcrab.jdialogue.parser.PCharacter; import pl.kotcrab.jdialogue.parser.Project; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Random; import java.util.StringTokenizer; import java.util.zip.GZIPInputStream; public class JDOMDialogueParser extends DialogueParser { private Random random = new Random(); private ArrayList<CallbackListener> listeners = new ArrayList<>(); private Project project; private String projectPath; private List<Element> elementList; private Element currentElement; private ComponentType currentComponentType; private int target = -1; private PCharacter currentCharacterData; private boolean lastCallbackCheckResult; private String[] msgText; private int currentMsg; public JDOMDialogueParser (DialogueLoader projectFile, int maxChars) { super(projectFile, maxChars); project = loadProject(projectFile); projectPath = projectFile.getFile().getParent() + File.separator; } @Override public void startSequence (String name) { try { SAXBuilder builder = new SAXBuilder(); Document document; if (project.isGzipExport()) document = builder.build(new GZIPInputStream(new FileInputStream(new File(projectPath + name + ".xml")))); else document = builder.build(new File(projectPath + name + ".xml")); Element rootNode = document.getRootElement(); elementList = rootNode.getChildren(); Element startNode = rootNode.getChildren("dStart").get(0); target = Integer.valueOf(startNode.getChildText("target0")); } catch (JDOMException | IOException e) { if (e.getMessage().contains("Invalid byte 1 of 1-byte UTF-8 sequence.")) throw new DialogueParserException("Error decoding file."); e.printStackTrace(); } } @Override public ComponentType processNextComponent () { if (target == -1) throw new DialogueParserException("target == -1. Did you call startSequence(String sequenceName) before calling processNextComponent()?"); currentElement = elementList.get(target); String name = currentElement.getName(); if (name.equals("dEnd")) currentComponentType = ComponentType.END; if (name.equals("dChoice")) { currentComponentType = ComponentType.CHOICE; msgText = new String[1]; msgText[0] = currentElement.getChildText("text"); } if (name.equals("dRandom")) currentComponentType = ComponentType.RANDOM; if (name.equals("dRelay")) currentComponentType = ComponentType.RELAY; if (name.equals("dText")) { int id = Integer.parseInt(currentElement.getChildText("character")); currentCharacterData = project.getCharacterList().get(project.getCharacterMap().get(id)); if (maxChars > 0) msgText = splitIntoLines(currentElement.getChildText("text")); else { msgText = new String[1]; msgText[0] = currentElement.getChildText("text"); } currentMsg = 0; currentComponentType = ComponentType.TEXT; } if (name.startsWith("dCallback")) { int id = Integer.parseInt(currentElement.getChildText("callback")); String callbackText = project.getCallbackList().get(project.getCallbackMap().get(id)).getName(); if (name.equals("dCallback")) { for (CallbackListener lis : listeners) lis.handleCallback(callbackText); currentComponentType = ComponentType.CALLBACK; } if (name.equals("dCallbackCheck")) { for (CallbackListener lis : listeners) lastCallbackCheckResult = lis.handleCallbackCheck(callbackText); currentComponentType = ComponentType.CBCHECK; } } return currentComponentType; } @Override public void moveToNextComponent (int target) { if (currentComponentType == ComponentType.RANDOM) { List<Element> randomList = currentElement.getChildren(); this.target = Integer.parseInt(randomList.get(1 + random.nextInt(randomList.size() - 1)).getText()); return; } if (currentComponentType != ComponentType.END) this.target = Integer.valueOf(currentElement.getChildText("target" + target)); } @Override public void moveToNextComponent () { if (currentComponentType == ComponentType.CBCHECK) { if (lastCallbackCheckResult == true) moveToNextComponent(0); else moveToNextComponent(1); return; } moveToNextComponent(0); } @Override public String getMsg () { if (maxChars > 0 && currentMsg < msgText.length) { String text = msgText[currentMsg]; currentMsg++; return text; } else return msgText[0]; } @Override public boolean isCurrentMsgFinished () { return currentMsg == msgText.length; } @Override public int getId () { return Integer.parseInt(currentElement.getChildText("id")); } @Override public String[] getChoiceData () { List<Element> choiceList = currentElement.getChildren("choiceData").get(0).getChildren("string"); String[] choiceData = new String[choiceList.size()]; for (int i = 0; i < choiceData.length; i++) choiceData[i] = choiceList.get(i).getText(); return choiceData; } @Override public PCharacter getCharacterData () { return currentCharacterData; } @Override public void addCallbackListener (CallbackListener listener) { listeners.add(listener); } @Override public boolean removeCallbackListener (CallbackListener listener) { return listeners.remove(listener); } private String[] splitIntoLines (String input) { StringTokenizer tok = new StringTokenizer(input, " "); StringBuilder output = new StringBuilder(input.length()); int lineLen = 0; while (tok.hasMoreTokens()) { String word = tok.nextToken(); while (word.length() > maxChars) { output.append(word.substring(0, maxChars - lineLen) + "\n"); word = word.substring(maxChars - lineLen); lineLen = 0; } if (lineLen + word.length() > maxChars) { output.append("\n"); lineLen = 0; } output.append(word + " "); lineLen += word.length() + 1; } return output.toString().split("\n"); } // =====================================LOADING PROJECT======================================== private Project loadProject (DialogueLoader projectFile) { String name = ""; boolean gzipExport = false; ArrayList<PCharacter> characterList = new ArrayList<>(); HashMap<Integer, Integer> characterMap = new HashMap<>(); ArrayList<PCallback> callbackList = new ArrayList<>(); HashMap<Integer, Integer> callbackMap = new HashMap<>(); try { SAXBuilder builder = new SAXBuilder(); Document document = builder.build(projectFile.getFile()); Element rootNode = document.getRootElement(); name = rootNode.getChildText("name"); gzipExport = Boolean.valueOf(rootNode.getChildText("gzipExport")); List<Element> characterElementList = rootNode.getChild("characterList").getChildren(); for (Element el : characterElementList) characterList.add(loadCharacter(el)); List<Element> callbackElementList = rootNode.getChild("callbackList").getChildren(); for (Element el : callbackElementList) callbackList.add(loadCallback(el)); List<Element> characterHaspMapElementList = rootNode.getChild("characterMap").getChildren(); for (Element el : characterHaspMapElementList) characterMap.put(Integer.valueOf(el.getChildren().get(0).getText()), Integer.valueOf(el.getChildren().get(1).getText())); List<Element> callbackHaspMapElementList = rootNode.getChild("callbackMap").getChildren(); for (Element el : callbackHaspMapElementList) callbackMap.put(Integer.valueOf(el.getChildren().get(0).getText()), Integer.valueOf(el.getChildren().get(1).getText())); } catch (JDOMException | IOException e) { throw new DialogueParserException("Chould not load project! Is your project.xml file correct?"); } return new Project(name, gzipExport, characterList, characterMap, callbackList, callbackMap); } private PCallback loadCallback (Element callbackElement) { int id = Integer.valueOf(callbackElement.getChildText("id")); String name = callbackElement.getChildText("name"); return new PCallback(id, name); } private PCharacter loadCharacter (Element characterElement) { int id = Integer.valueOf(characterElement.getChildText("id")); String name = characterElement.getChildText("name"); String textureName = characterElement.getChildText("textureName"); return new PCharacter(id, name, textureName); } }